From 6f9c6626855b224df5d4bbdbc5bbe0be2e5bd68b Mon Sep 17 00:00:00 2001 From: Brian DeHamer Date: Wed, 6 Dec 2023 12:04:28 -0800 Subject: [PATCH] extract verification code into separate package (#886) Signed-off-by: Brian DeHamer --- .changeset/swift-balloons-pay.md | 5 + .eslintignore | 1 + .github/workflows/ci.yml | 2 +- package-lock.json | 40 + packages/core/src/dsse.ts | 14 +- packages/core/src/index.ts | 3 +- packages/core/src/x509/index.ts | 3 +- packages/verify/README.md | 9 + packages/verify/jest.config.js | 27 + packages/verify/package.json | 36 + .../__fixtures__/bundles/v01/dsse.ts | 783 ++++++++++++++++++ .../__fixtures__/bundles/v01/index.ts | 4 + .../__fixtures__/bundles/v01/signature.ts | 570 +++++++++++++ .../__fixtures__/bundles/v02/index.ts | 32 + .../__fixtures__/bundles/v02/signature.ts | 166 ++++ .../src/__tests__/__fixtures__/certs.ts | 102 +++ .../src/__tests__/__fixtures__/trust.ts | 107 +++ .../verify/src/__tests__/bundle/dsse.test.ts | 100 +++ .../verify/src/__tests__/bundle/index.test.ts | 56 ++ .../src/__tests__/bundle/message.test.ts | 80 ++ packages/verify/src/__tests__/index.test.ts | 48 ++ .../src/__tests__/key/certificate.test.ts | 141 ++++ .../verify/src/__tests__/key/index.test.ts | 183 ++++ packages/verify/src/__tests__/key/sct.test.ts | 107 +++ .../__tests__/timestamp/checkpoint.test.ts | 237 ++++++ .../src/__tests__/timestamp/index.test.ts | 82 ++ .../src/__tests__/timestamp/merkle.test.ts | 210 +++++ .../src/__tests__/timestamp/set.test.ts | 119 +++ .../verify/src/__tests__/tlog/dsse.test.ts | 130 +++ .../src/__tests__/tlog/hashedrekord.test.ts | 134 +++ .../verify/src/__tests__/tlog/index.test.ts | 65 ++ .../verify/src/__tests__/tlog/intoto.test.ts | 130 +++ .../verify/src/__tests__/trust/filter.test.ts | 72 ++ .../verify/src/__tests__/trust/index.test.ts | 78 ++ .../verify/src/__tests__/verifier.test.ts | 143 ++++ packages/verify/src/bundle/dsse.ts | 50 ++ packages/verify/src/bundle/index.ts | 75 ++ packages/verify/src/bundle/message.ts | 43 + packages/verify/src/error.ts | 47 ++ packages/verify/src/index.ts | 20 + packages/verify/src/key/certificate.ts | 269 ++++++ packages/verify/src/key/index.ts | 91 ++ packages/verify/src/key/sct.ts | 104 +++ packages/verify/src/shared.types.ts | 81 ++ packages/verify/src/timestamp/checkpoint.ts | 207 +++++ packages/verify/src/timestamp/index.ts | 92 ++ packages/verify/src/timestamp/merkle.ts | 127 +++ packages/verify/src/timestamp/set.ts | 80 ++ packages/verify/src/tlog/dsse.ts | 69 ++ packages/verify/src/tlog/hashedrekord.ts | 62 ++ packages/verify/src/tlog/index.ts | 56 ++ packages/verify/src/tlog/intoto.ts | 76 ++ packages/verify/src/trust/filter.ts | 58 ++ packages/verify/src/trust/index.ts | 103 +++ packages/verify/src/trust/trust.types.ts | 48 ++ packages/verify/src/verifier.ts | 162 ++++ packages/verify/tsconfig.json | 17 + 57 files changed, 5948 insertions(+), 8 deletions(-) create mode 100644 .changeset/swift-balloons-pay.md create mode 100644 packages/verify/README.md create mode 100644 packages/verify/jest.config.js create mode 100644 packages/verify/package.json create mode 100644 packages/verify/src/__tests__/__fixtures__/bundles/v01/dsse.ts create mode 100644 packages/verify/src/__tests__/__fixtures__/bundles/v01/index.ts create mode 100644 packages/verify/src/__tests__/__fixtures__/bundles/v01/signature.ts create mode 100644 packages/verify/src/__tests__/__fixtures__/bundles/v02/index.ts create mode 100644 packages/verify/src/__tests__/__fixtures__/bundles/v02/signature.ts create mode 100644 packages/verify/src/__tests__/__fixtures__/certs.ts create mode 100644 packages/verify/src/__tests__/__fixtures__/trust.ts create mode 100644 packages/verify/src/__tests__/bundle/dsse.test.ts create mode 100644 packages/verify/src/__tests__/bundle/index.test.ts create mode 100644 packages/verify/src/__tests__/bundle/message.test.ts create mode 100644 packages/verify/src/__tests__/index.test.ts create mode 100644 packages/verify/src/__tests__/key/certificate.test.ts create mode 100644 packages/verify/src/__tests__/key/index.test.ts create mode 100644 packages/verify/src/__tests__/key/sct.test.ts create mode 100644 packages/verify/src/__tests__/timestamp/checkpoint.test.ts create mode 100644 packages/verify/src/__tests__/timestamp/index.test.ts create mode 100644 packages/verify/src/__tests__/timestamp/merkle.test.ts create mode 100644 packages/verify/src/__tests__/timestamp/set.test.ts create mode 100644 packages/verify/src/__tests__/tlog/dsse.test.ts create mode 100644 packages/verify/src/__tests__/tlog/hashedrekord.test.ts create mode 100644 packages/verify/src/__tests__/tlog/index.test.ts create mode 100644 packages/verify/src/__tests__/tlog/intoto.test.ts create mode 100644 packages/verify/src/__tests__/trust/filter.test.ts create mode 100644 packages/verify/src/__tests__/trust/index.test.ts create mode 100644 packages/verify/src/__tests__/verifier.test.ts create mode 100644 packages/verify/src/bundle/dsse.ts create mode 100644 packages/verify/src/bundle/index.ts create mode 100644 packages/verify/src/bundle/message.ts create mode 100644 packages/verify/src/error.ts create mode 100644 packages/verify/src/index.ts create mode 100644 packages/verify/src/key/certificate.ts create mode 100644 packages/verify/src/key/index.ts create mode 100644 packages/verify/src/key/sct.ts create mode 100644 packages/verify/src/shared.types.ts create mode 100644 packages/verify/src/timestamp/checkpoint.ts create mode 100644 packages/verify/src/timestamp/index.ts create mode 100644 packages/verify/src/timestamp/merkle.ts create mode 100644 packages/verify/src/timestamp/set.ts create mode 100644 packages/verify/src/tlog/dsse.ts create mode 100644 packages/verify/src/tlog/hashedrekord.ts create mode 100644 packages/verify/src/tlog/index.ts create mode 100644 packages/verify/src/tlog/intoto.ts create mode 100644 packages/verify/src/trust/filter.ts create mode 100644 packages/verify/src/trust/index.ts create mode 100644 packages/verify/src/trust/trust.types.ts create mode 100644 packages/verify/src/verifier.ts create mode 100644 packages/verify/tsconfig.json diff --git a/.changeset/swift-balloons-pay.md b/.changeset/swift-balloons-pay.md new file mode 100644 index 00000000..4c5b9a71 --- /dev/null +++ b/.changeset/swift-balloons-pay.md @@ -0,0 +1,5 @@ +--- +'@sigstore/verify': minor +--- + +Extract verification code into dedicated package diff --git a/.eslintignore b/.eslintignore index 5ee03bc2..8f2e9bd3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ node_modules dist **/__generated__ +**/__fixtures__ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15ae5d69..7439f894 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: - os: ubuntu-latest shell: bash jest-cache: /tmp/jest - - os: macos-latest + - os: macos-latest-large shell: bash jest-cache: /tmp/jest - os: windows-latest diff --git a/package-lock.json b/package-lock.json index 50cbfc84..921bb148 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2998,6 +2998,10 @@ "resolved": "packages/tuf", "link": true }, + "node_modules/@sigstore/verify": { + "resolved": "packages/verify", + "link": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -14187,6 +14191,27 @@ "engines": { "node": "^16.14.0 || >=18.0.0" } + }, + "packages/verify": { + "name": "@sigstore/verify", + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.1.0", + "@sigstore/core": "^0.1.0", + "@sigstore/protobuf-specs": "^0.1.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "packages/verify/node_modules/@sigstore/protobuf-specs": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", + "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } } }, "dependencies": { @@ -16595,6 +16620,21 @@ "tuf-js": "^2.1.0" } }, + "@sigstore/verify": { + "version": "file:packages/verify", + "requires": { + "@sigstore/bundle": "^2.1.0", + "@sigstore/core": "^0.1.0", + "@sigstore/protobuf-specs": "^0.1.0" + }, + "dependencies": { + "@sigstore/protobuf-specs": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", + "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==" + } + } + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", diff --git a/packages/core/src/dsse.ts b/packages/core/src/dsse.ts index 700806e4..a625f584 100644 --- a/packages/core/src/dsse.ts +++ b/packages/core/src/dsse.ts @@ -17,9 +17,13 @@ const PAE_PREFIX = 'DSSEv1'; // DSSE Pre-Authentication Encoding export function preAuthEncoding(payloadType: string, payload: Buffer): Buffer { - const prefix = Buffer.from( - `${PAE_PREFIX} ${payloadType.length} ${payloadType} ${payload.length} `, - 'ascii' - ); - return Buffer.concat([prefix, payload]); + const prefix = [ + PAE_PREFIX, + payloadType.length, + payloadType, + payload.length, + '', + ].join(' '); + + return Buffer.concat([Buffer.from(prefix, 'ascii'), payload]); } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 013e2b68..6e087b9b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -19,4 +19,5 @@ export * as dsse from './dsse'; export * as encoding from './encoding'; export * as json from './json'; export * as pem from './pem'; -export { X509Certificate } from './x509'; +export { ByteStream } from './stream'; +export { EXTENSION_OID_SCT, X509Certificate, X509SCTExtension } from './x509'; diff --git a/packages/core/src/x509/index.ts b/packages/core/src/x509/index.ts index 698f409a..9fb261c0 100644 --- a/packages/core/src/x509/index.ts +++ b/packages/core/src/x509/index.ts @@ -14,4 +14,5 @@ See the License for the specific language governing permissions and limitations under the License. */ -export { X509Certificate } from './cert'; +export { EXTENSION_OID_SCT, X509Certificate } from './cert'; +export { X509SCTExtension } from './ext'; diff --git a/packages/verify/README.md b/packages/verify/README.md new file mode 100644 index 00000000..baa357c0 --- /dev/null +++ b/packages/verify/README.md @@ -0,0 +1,9 @@ +# @sigstore/verify · [![npm version](https://img.shields.io/npm/v/@sigstore/verify.svg?style=flat)](https://www.npmjs.com/package/@sigstore/verify) [![CI Status](https://github.com/sigstore/sigstore-js/workflows/CI/badge.svg)](https://github.com/sigstore/sigstore-js/actions/workflows/ci.yml) [![Smoke Test Status](https://github.com/sigstore/sigstore-js/workflows/smoke-test/badge.svg)](https://github.com/sigstore/sigstore-js/actions/workflows/smoke-test.yml) + +A library for verifying [Sigstore][1] signatures. + +## Prerequisites + +- Node.js version >= 16.14.0 + +[1]: https://www.sigstore.dev diff --git a/packages/verify/jest.config.js b/packages/verify/jest.config.js new file mode 100644 index 00000000..ad68cc98 --- /dev/null +++ b/packages/verify/jest.config.js @@ -0,0 +1,27 @@ +/* +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. +*/ +const base = require('../../jest.config.base'); + +module.exports = { + ...base, + displayName: 'verify', + setupFilesAfterEnv: ['@sigstore/jest/all'], + testPathIgnorePatterns: [ + '/dist/', + '/src/__tests__/__fixtures__', + ], + coveragePathIgnorePatterns: ['__fixtures__'], +}; diff --git a/packages/verify/package.json b/packages/verify/package.json new file mode 100644 index 00000000..c19f133f --- /dev/null +++ b/packages/verify/package.json @@ -0,0 +1,36 @@ +{ + "name": "@sigstore/verify", + "version": "0.0.0", + "description": "Verification of Sigstore signatures", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "clean": "shx rm -rf dist *.tsbuildinfo", + "build": "tsc --build", + "test": "jest" + }, + "files": [ + "dist" + ], + "author": "bdehamer@github.com", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/sigstore/sigstore-js.git" + }, + "bugs": { + "url": "https://github.com/sigstore/sigstore-js/issues" + }, + "homepage": "https://github.com/sigstore/sigstore-js/tree/main/packages/verify#readme", + "publishConfig": { + "provenance": true + }, + "dependencies": { + "@sigstore/protobuf-specs": "^0.1.0", + "@sigstore/bundle": "^2.1.0", + "@sigstore/core": "^0.1.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } +} diff --git a/packages/verify/src/__tests__/__fixtures__/bundles/v01/dsse.ts b/packages/verify/src/__tests__/__fixtures__/bundles/v01/dsse.ts new file mode 100644 index 00000000..0949853f --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/bundles/v01/dsse.ts @@ -0,0 +1,783 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/////////////////////////////////////////////////////////////////////////////// +// VALID BUNDLES +/////////////////////////////////////////////////////////////////////////////// + +// Valid DSSE bundle signed with a Fulcio signing certificate +const validBundleWithSigningCert = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +// Valid DSSE bundle signed with a public key +const validBundleWithPublicKey = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + publicKey: { + hint: '9a76331edc1cfd3933040996615b1c06adbe6f9b4f11df4106dcceb66e3bdb1b', + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +const validBundleWithNoTLogEntries = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +const validBundleWithDSSETLogEntry = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIIC0TCCAlagAwIBAgIUOBERpxhVuZTd8XrNJheYDQ9iszAwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNjA1MTc1MjU0WhcNMjMwNjA1MTgwMjU0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExoIQxR/RxXl20dAM7pcKlC5fEwRexmHMCzsXaPpUpror/AQlst/WGcvHkbtGXciBWVkrnxnHo1I5O+73FxBrRaOCAXUwggFxMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUBhcYgqIg8VwrdSF1kVTOtPPFNSkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMC4GCisGAQQBg78wAQgEIAweaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGIjLDD8QAABAMARzBFAiAU40oVa45EOf1Miq1b+PEFFG8a/51N/ovrHcy74w9elgIhAJ0JMviR309CWzosD9k/iq9s+Ij3xjNdru4gmsf+ejV2MAoGCCqGSM49BAMDA2kAMGYCMQDK7j57m6kiLX6b/0akP4SLewDSKrpf7Nx+PdbeAe95TyQGtZJC+Z+3jTIp9zosxpECMQDqPIQLDyokUecisWPVJ2GUxsT0yw1hp3LYu63sdGxUSV68ms7QvWUKkfaBRGq+sNk=', + }, + ], + }, + tlogEntries: [ + { + logIndex: '22797666', + logId: { + keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=', + }, + kindVersion: { + kind: 'dsse', + version: '0.0.1', + }, + integratedTime: '1685987575', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIHhAfEwtfkiRK/XD1EbjArDzf9svz75oqUqfT6Mha6PHAiBE+ibnkU7x/AgKMshVT23+DLc3+OtTozW5eulQKrBQnw==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZjRjMTc1ZTVlZjQ2YjEyMzc4NzQ3MDZkYjFhYzE1YmY5ZGYzYTg5MGJlNmY1MmEyNzY0Y2QyZGFiMzJjZWQwNyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVZQ0lRRHFLRHQ2MTk3dnFjYkM3Rys0YkF6NWkrYitnSUVIRjdiMG1uWkJJejZvMmdJaEFPNG84WFdBZFdZRGUwRjZOTHVpK3hLSVA3a2hvQVUzNDZnSmYwUzNxVFpvIiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNd1ZFTkRRV3hoWjBGM1NVSkJaMGxWVDBKRlVuQjRhRloxV2xSa09GaHlUa3BvWlZsRVVUbHBjM3BCZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNXFRVEZOVkdNeFRXcFZNRmRvWTA1TmFrMTNUbXBCTVUxVVozZE5hbFV3VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjRiMGxSZUZJdlVuaFliREl3WkVGTk4zQmpTMnhETldaRmQxSmxlRzFJVFVONmMxZ0tZVkJ3VlhCeWIzSXZRVkZzYzNRdlYwZGpka2hyWW5SSFdHTnBRbGRXYTNKdWVHNUliekZKTlU4ck56TkdlRUp5VW1GUFEwRllWWGRuWjBaNFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVkNhR05aQ21keFNXYzRWbmR5WkZOR01XdFdWRTkwVUZCR1RsTnJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMGgzV1VSV1VqQlNRVkZJTDBKQ1ZYZEZORVZTV1c1S2NGbFhOVUZhUjFadldWY3hiR05wTldwaU1qQjNURUZaUzB0M1dVSkNRVWRFZG5wQlFncEJVVkZsWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREo0ZGxveWJIVk1NamxvWkZoU2IwMURORWREYVhOSFFWRlJRbWMzT0hkQlVXZEZDa2xCZDJWaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW5oMldqSnNkVXd5T1doa1dGSnZUVWxIUzBKbmIzSkNaMFZGUVdSYU5VRm5VVU1LUWtoM1JXVm5RalJCU0ZsQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpWQkxNeTlvTkhCNVowTTRjRGR2TkVGQlFVZEpha3hFUkFvNFVVRkJRa0ZOUVZKNlFrWkJhVUZWTkRCdlZtRTBOVVZQWmpGTmFYRXhZaXRRUlVaR1J6aGhMelV4VGk5dmRuSklZM2szTkhjNVpXeG5TV2hCU2pCS0NrMTJhVkl6TURsRFYzcHZjMFE1YXk5cGNUbHpLMGxxTTNocVRtUnlkVFJuYlhObUsyVnFWakpOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpyUVUxSFdVTUtUVkZFU3pkcU5UZHRObXRwVEZnMllpOHdZV3RRTkZOTVpYZEVVMHR5Y0dZM1RuZ3JVR1JpWlVGbE9UVlVlVkZIZEZwS1F5dGFLek5xVkVsd09YcHZjd3A0Y0VWRFRWRkVjVkJKVVV4RWVXOXJWV1ZqYVhOWFVGWktNa2RWZUhOVU1IbDNNV2h3TTB4WmRUWXpjMlJIZUZWVFZqWTRiWE0zVVhaWFZVdHJabUZDQ2xKSGNTdHpUbXM5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0=', + }, + ], + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEYCIQDqKDt6197vqcbC7G+4bAz5i+b+gIEHF7b0mnZBIz6o2gIhAO4o8XWAdWYDe0F6NLui+xKIP7khoAU346gJf0S3qTZo', + keyid: '', + }, + ], + }, +}; + +// Public key material for verifying the above bundle +const publicKey = `-----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJf + VQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeg== + -----END PUBLIC KEY-----`; + +/////////////////////////////////////////////////////////////////////////////// +// INVALID BUNDLES +/////////////////////////////////////////////////////////////////////////////// + +// Invalid DSSE bundle - payload doesn't match signature +const invalidBadSignatureTLogIntoto = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'Z29vZGJ5ZSwgd29ybGQh', // Valued altered + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - payload doesn't match signature +const invalidBadSignatureTLogDSSE = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIIC0TCCAlagAwIBAgIUOBERpxhVuZTd8XrNJheYDQ9iszAwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNjA1MTc1MjU0WhcNMjMwNjA1MTgwMjU0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExoIQxR/RxXl20dAM7pcKlC5fEwRexmHMCzsXaPpUpror/AQlst/WGcvHkbtGXciBWVkrnxnHo1I5O+73FxBrRaOCAXUwggFxMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUBhcYgqIg8VwrdSF1kVTOtPPFNSkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMC4GCisGAQQBg78wAQgEIAweaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGIjLDD8QAABAMARzBFAiAU40oVa45EOf1Miq1b+PEFFG8a/51N/ovrHcy74w9elgIhAJ0JMviR309CWzosD9k/iq9s+Ij3xjNdru4gmsf+ejV2MAoGCCqGSM49BAMDA2kAMGYCMQDK7j57m6kiLX6b/0akP4SLewDSKrpf7Nx+PdbeAe95TyQGtZJC+Z+3jTIp9zosxpECMQDqPIQLDyokUecisWPVJ2GUxsT0yw1hp3LYu63sdGxUSV68ms7QvWUKkfaBRGq+sNk=', + }, + ], + }, + tlogEntries: [ + { + logIndex: '22797666', + logId: { + keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=', + }, + kindVersion: { + kind: 'dsse', + version: '0.0.1', + }, + integratedTime: '1685987575', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIHhAfEwtfkiRK/XD1EbjArDzf9svz75oqUqfT6Mha6PHAiBE+ibnkU7x/AgKMshVT23+DLc3+OtTozW5eulQKrBQnw==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZjRjMTc1ZTVlZjQ2YjEyMzc4NzQ3MDZkYjFhYzE1YmY5ZGYzYTg5MGJlNmY1MmEyNzY0Y2QyZGFiMzJjZWQwNyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVZQ0lRRHFLRHQ2MTk3dnFjYkM3Rys0YkF6NWkrYitnSUVIRjdiMG1uWkJJejZvMmdJaEFPNG84WFdBZFdZRGUwRjZOTHVpK3hLSVA3a2hvQVUzNDZnSmYwUzNxVFpvIiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNd1ZFTkRRV3hoWjBGM1NVSkJaMGxWVDBKRlVuQjRhRloxV2xSa09GaHlUa3BvWlZsRVVUbHBjM3BCZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNXFRVEZOVkdNeFRXcFZNRmRvWTA1TmFrMTNUbXBCTVUxVVozZE5hbFV3VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjRiMGxSZUZJdlVuaFliREl3WkVGTk4zQmpTMnhETldaRmQxSmxlRzFJVFVONmMxZ0tZVkJ3VlhCeWIzSXZRVkZzYzNRdlYwZGpka2hyWW5SSFdHTnBRbGRXYTNKdWVHNUliekZKTlU4ck56TkdlRUp5VW1GUFEwRllWWGRuWjBaNFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVkNhR05aQ21keFNXYzRWbmR5WkZOR01XdFdWRTkwVUZCR1RsTnJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMGgzV1VSV1VqQlNRVkZJTDBKQ1ZYZEZORVZTV1c1S2NGbFhOVUZhUjFadldWY3hiR05wTldwaU1qQjNURUZaUzB0M1dVSkNRVWRFZG5wQlFncEJVVkZsWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREo0ZGxveWJIVk1NamxvWkZoU2IwMURORWREYVhOSFFWRlJRbWMzT0hkQlVXZEZDa2xCZDJWaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW5oMldqSnNkVXd5T1doa1dGSnZUVWxIUzBKbmIzSkNaMFZGUVdSYU5VRm5VVU1LUWtoM1JXVm5RalJCU0ZsQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpWQkxNeTlvTkhCNVowTTRjRGR2TkVGQlFVZEpha3hFUkFvNFVVRkJRa0ZOUVZKNlFrWkJhVUZWTkRCdlZtRTBOVVZQWmpGTmFYRXhZaXRRUlVaR1J6aGhMelV4VGk5dmRuSklZM2szTkhjNVpXeG5TV2hCU2pCS0NrMTJhVkl6TURsRFYzcHZjMFE1YXk5cGNUbHpLMGxxTTNocVRtUnlkVFJuYlhObUsyVnFWakpOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpyUVUxSFdVTUtUVkZFU3pkcU5UZHRObXRwVEZnMllpOHdZV3RRTkZOTVpYZEVVMHR5Y0dZM1RuZ3JVR1JpWlVGbE9UVlVlVkZIZEZwS1F5dGFLek5xVkVsd09YcHZjd3A0Y0VWRFRWRkVjVkJKVVV4RWVXOXJWV1ZqYVhOWFVGWktNa2RWZUhOVU1IbDNNV2h3TTB4WmRUWXpjMlJIZUZWVFZqWTRiWE0zVVhaWFZVdHJabUZDQ2xKSGNTdHpUbXM5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0=', + }, + ], + }, + dsseEnvelope: { + payload: 'Z29vZGJ5ZSwgd29ybGQh', // Value altered + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEYCIQDqKDt6197vqcbC7G+4bAz5i+b+gIEHF7b0mnZBIz6o2gIhAO4o8XWAdWYDe0F6NLui+xKIP7khoAU346gJf0S3qTZo', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - no signature +const invalidNoSignature = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [], // Altered value + }, +}; + +// Invalid DSSE bundle - missing kindVersion in tlog entry +const invalidMissingKindVersion = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - missing logId in tlog entry +const invalidMissingLogId = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + integratedTime: '1667948287', + kindVersion: { kind: 'intoto', version: '0.0.2' }, + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - integratedTime altered, invalidating SET +const invalidSET = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1', // Altered value + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - version in tlogEntry don't match canonicalizedBody +const invalidTLogVersionMismatch = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.X' }, // Altered value + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - the signature in the canonicalized tlog body doesn't +// match the signature in the DSSE envelope. +const invalidIntotoIncorrectSigInTLogBody = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUSkFzCwp0c8PRty4So7bizLRExZswCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDAyOTI1WhcNMjIxMTA5MDAzOTI1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEj/l/5Pi+TWUkamLY1UH9yq0XgeH818M38IQm6rYdeT0JLLRozmzqGGUjoAFw1G69fBTAP0ae22V93BFT2JGYjqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUZzrPkPRD8aCDHvxscd7v6T5fOZMwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWcqsQgAABAMARzBFAiEA0XEwx48N/TZneFxEV7RwbVIhROPOMJYo1XEoN4sd98wCIGlL4RnI8JVHdYbzbZLHUIr7+5I1Dl//zUJBh4A3YPnzMAoGCCqGSM49BAMDA2gAMGUCMQDyFvX31sp+olif92nr/w52KLmtPoWVHxtGeaxuNAtQ7DFdtSDvXGenUT+fqALnAikCMD/hbwNuQl9kr2nlZ96GlT3eMCl78o0D2QKjx2Do1JC6lqG2hDM61FG6Znx7T5VWfQ==', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6755099', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667953968', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIACiV07L0UdN6++1ljz5fbK9L5w3epXGvb/tRRmg+LlOAiEA+ksZQyovogZd2Y2YH0A9iXle3Cee90F/TU2h9DcdkbA=', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU52YWtORFFXbGxaMEYzU1VKQlowbFZXbXMyUWpGRE56UjZURXBMTDBOblMwMWhOV3AyVmtaWk9IZE5kME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUVk5SRUY2VFdwUk0xZG9ZMDVOYWtsNFRWUkJOVTFFUVRCTmFsRXpWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWb1psWkVla0ZoZWxjM1RERjVPREE1TVdObFNtWmFUbU16VmxWTll6VlpRVEpNWTNvS1pqUjNhRXBQVjJ4RGNuZExXVE5IUXpKTlVpdEVWbEp6YVZGck1HRTBWRUpWYkhWT2F6Sk5hM1pOZHpodE9VdEllalpQUTBGVldYZG5aMFpEVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWd2FFSjFDa2xLTjBGbWVucGlMMVpTUmt0WWNIRm5aMUUzUW1kQmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIweENaMjl5UW1kRlJVRmtXalZCWjFGRENrSklNRVZsZDBJMVFVaGpRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkak0wUUthMmRCUVVKQlRVRlRSRUpIUVdsRlFTdFliemxHZGl0dVpHTlpNazlzTldJeVRUaEZTSGhUTTBob2IwSmlTalpTUWpKVVkzWXZURTAwZVZGRFNWRkVaQXBzY1c1U2JFbzVWWEJWTkdwT2VXaGhRbE5FWjBSMGVEY3JPRTVSTVVkdU56RTBjMlJZVVdGdlJtcEJTMEpuWjNGb2EycFBVRkZSUkVGM1RuQkJSRUp0Q2tGcVJVRnViWGRXWlZGMVJYTnhaMVV4U0hKS1RrSlZaSEIxTlhVMlozZFhSSFExVGxWR09YcFphWFI1YUZWTlQwZFdTMWRRUmxKek1YcFJlVEkyYldjS1JtZ3hjMEZxUlVGM1ZqRlRabGRtTnk5QlNUWllRVzVZYUdNMFZrSllRaTlKVFhkVlFuSmxjbEJ4WVVwS2NWRTNSblF4ZGtKQ2JpOXFSRkpaYVhOelJ3cDFjVWx5V1VGWFN3b3RMUzB0TFVWT1JDQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENnPT0iLCJzaWciOiJUVVZWUTBsQ1ZUSldiMFUxV0c5VE9EQkZWamhxVHpCU2RsRmhWRzFsUWtWTFIwVjBWVUZKYTJKa1FsUnNXV3QxUVdsRlFXNDJkazl6VkRsNlJWcHpSemhpV1ROdlJUVTBXVWhsTHpWQmNFTXZXVFZwVFZCWWRqaGhjREIyUlZFOSJ9XX0sImhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5ODQ2YjRlMDU0ZWYwYzVlZWIwNzA1NDQ0N2I2OWQ2ODgxZWRkNmIwMTZlOWU4Nzg0M2YyMzVhOWMyMDFkMzAzIn0sInBheWxvYWRIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNjhlNjU2YjI1MWU2N2U4MzU4YmVmODQ4M2FiMGQ1MWM2NjE5ZjNlN2ExYTlmMGU3NTgzOGQ0MWZmMzY4ZjcyOCJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEYCIQDBfvwEED9oUrvTjZ7cJrZkE073IqG8V8r5I1wMY3YAJwIhAJ+UXICfI5rs7Jl2oFEAK8KG+asRWQA+ZkRA1jW2lUYO', + keyid: '', + }, + ], + }, +}; + +const invalidDSSEIncorrectSigInTLogBody = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIIC0TCCAlagAwIBAgIUOBERpxhVuZTd8XrNJheYDQ9iszAwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNjA1MTc1MjU0WhcNMjMwNjA1MTgwMjU0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExoIQxR/RxXl20dAM7pcKlC5fEwRexmHMCzsXaPpUpror/AQlst/WGcvHkbtGXciBWVkrnxnHo1I5O+73FxBrRaOCAXUwggFxMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUBhcYgqIg8VwrdSF1kVTOtPPFNSkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMC4GCisGAQQBg78wAQgEIAweaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGIjLDD8QAABAMARzBFAiAU40oVa45EOf1Miq1b+PEFFG8a/51N/ovrHcy74w9elgIhAJ0JMviR309CWzosD9k/iq9s+Ij3xjNdru4gmsf+ejV2MAoGCCqGSM49BAMDA2kAMGYCMQDK7j57m6kiLX6b/0akP4SLewDSKrpf7Nx+PdbeAe95TyQGtZJC+Z+3jTIp9zosxpECMQDqPIQLDyokUecisWPVJ2GUxsT0yw1hp3LYu63sdGxUSV68ms7QvWUKkfaBRGq+sNk=', + }, + ], + }, + tlogEntries: [ + { + logIndex: '22797666', + logId: { + keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=', + }, + kindVersion: { + kind: 'dsse', + version: '0.0.1', + }, + integratedTime: '1685987575', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIHhAfEwtfkiRK/XD1EbjArDzf9svz75oqUqfT6Mha6PHAiBE+ibnkU7x/AgKMshVT23+DLc3+OtTozW5eulQKrBQnw==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZjRjMTc1ZTVlZjQ2YjEyMzc4NzQ3MDZkYjFhYzE1YmY5ZGYzYTg5MGJlNmY1MmEyNzY0Y2QyZGFiMzJjZWQwNyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVZQ0lRRHFLRHQ2MTk3dnFjYkM3Rys0YkF6NWkrYitnSUVIRjhiMG1uWkJJejZvMmdJaEFPNG84WFdBZFdZRGUwRjZOTHVpK3hLSVA3a2hvQVUzNDZnSmYwUzNxVFpvIiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNd1ZFTkRRV3hoWjBGM1NVSkJaMGxWVDBKRlVuQjRhRloxV2xSa09GaHlUa3BvWlZsRVVUbHBjM3BCZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNXFRVEZOVkdNeFRXcFZNRmRvWTA1TmFrMTNUbXBCTVUxVVozZE5hbFV3VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjRiMGxSZUZJdlVuaFliREl3WkVGTk4zQmpTMnhETldaRmQxSmxlRzFJVFVONmMxZ0tZVkJ3VlhCeWIzSXZRVkZzYzNRdlYwZGpka2hyWW5SSFdHTnBRbGRXYTNKdWVHNUliekZKTlU4ck56TkdlRUp5VW1GUFEwRllWWGRuWjBaNFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVkNhR05aQ21keFNXYzRWbmR5WkZOR01XdFdWRTkwVUZCR1RsTnJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMGgzV1VSV1VqQlNRVkZJTDBKQ1ZYZEZORVZTV1c1S2NGbFhOVUZhUjFadldWY3hiR05wTldwaU1qQjNURUZaUzB0M1dVSkNRVWRFZG5wQlFncEJVVkZsWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREo0ZGxveWJIVk1NamxvWkZoU2IwMURORWREYVhOSFFWRlJRbWMzT0hkQlVXZEZDa2xCZDJWaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW5oMldqSnNkVXd5T1doa1dGSnZUVWxIUzBKbmIzSkNaMFZGUVdSYU5VRm5VVU1LUWtoM1JXVm5RalJCU0ZsQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpWQkxNeTlvTkhCNVowTTRjRGR2TkVGQlFVZEpha3hFUkFvNFVVRkJRa0ZOUVZKNlFrWkJhVUZWTkRCdlZtRTBOVVZQWmpGTmFYRXhZaXRRUlVaR1J6aGhMelV4VGk5dmRuSklZM2szTkhjNVpXeG5TV2hCU2pCS0NrMTJhVkl6TURsRFYzcHZjMFE1YXk5cGNUbHpLMGxxTTNocVRtUnlkVFJuYlhObUsyVnFWakpOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpyUVUxSFdVTUtUVkZFU3pkcU5UZHRObXRwVEZnMllpOHdZV3RRTkZOTVpYZEVVMHR5Y0dZM1RuZ3JVR1JpWlVGbE9UVlVlVkZIZEZwS1F5dGFLek5xVkVsd09YcHZjd3A0Y0VWRFRWRkVjVkJKVVV4RWVXOXJWV1ZqYVhOWFVGWktNa2RWZUhOVU1IbDNNV2h3TTB4WmRUWXpjMlJIZUZWVFZqWTRiWE0zVVhaWFZVdHJabUZDQ2xKSGNTdHpUbXM5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0K', + }, + ], + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEYCIQDqKDt6197vqcbC7G+4bAz5i+b+gIEHF7b0mnZBIz6o2gIhAO4o8XWAdWYDe0F6NLui+xKIP7khoAU346gJf0S3qTZo', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - there are more signatures in the canonicalized tlog +// body than in the DSSE envelope. +const invalidIntotoTooManySigsInTLogBody = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICojCCAiegAwIBAgIUZk6B1C74zLJK/CgKMa5jvVFY8wMwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDAzMjQ3WhcNMjIxMTA5MDA0MjQ3WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhfVDzAazW7L1y8091ceJfZNc3VUMc5YA2Lczf4whJOWlCrwKY3GC2MR+DVRsiQk0a4TBUluNk2MkvMw8m9KHz6OCAUYwggFCMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUphBuIJ7Afzzb/VRFKXpqggQ7BgAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWc3DkgAABAMASDBGAiEA+Xo9Fv+ndcY2Ol5b2M8EHxS3HhoBbJ6RB2Tcv/LM4yQCIQDdlqnRlJ9UpU4jNyhaBSDgDtx7+8NQ1Gn714sdXQaoFjAKBggqhkjOPQQDAwNpADBmAjEAnmwVeQuEsqgU1HrJNBUdpu5u6gwWDt5NUF9zYityhUMOGVKWPFRs1zQy26mgFh1sAjEAwV1SfWf7/AI6XAnXhc4VBXB/IMwUBrerPqaJJqQ7Ft1vBBn/jDRYissGuqIrYAWK', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6755116', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667953985', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIQDnXvkNqIJTkodCwldyuc/FS733oqv2gsPPbMqqp1SJvQIgfC/6o89CElY8dLwONkQl8VQOFFYuxAltupqy8fLqLKs=', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU52UkVORFFXbGhaMEYzU1VKQlowbFZVMnRHZWtOM2NEQmpPRkJTZEhrMFUyODNZbWw2VEZKRmVGcHpkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUVk5SRUY1VDFSSk1WZG9ZMDVOYWtsNFRWUkJOVTFFUVhwUFZFa3hWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWcUwyd3ZOVkJwSzFSWFZXdGhiVXhaTVZWSU9YbHhNRmhuWlVnNE1UaE5NemhKVVcwS05uSlpaR1ZVTUVwTVRGSnZlbTE2Y1VkSFZXcHZRVVozTVVjMk9XWkNWRUZRTUdGbE1qSldPVE5DUmxReVNrZFphbkZQUTBGVlZYZG5aMFpDVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWYWVuSlFDbXRRVWtRNFlVTkVTSFo0YzJOa04zWTJWRFZtVDFwTmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwdENaMjl5UW1kRlJVRmtXalZCWjFGRENrSklkMFZsWjBJMFFVaFpRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkamNYTUtVV2RCUVVKQlRVRlNla0pHUVdsRlFUQllSWGQ0TkRoT0wxUmFibVZHZUVWV04xSjNZbFpKYUZKUFVFOU5TbGx2TVZoRmIwNDBjMlE1T0hkRFNVZHNUQW8wVW01Sk9FcFdTR1JaWW5waVdreElWVWx5TnlzMVNURkViQzh2ZWxWS1FtZzBRVE5aVUc1NlRVRnZSME5EY1VkVFRUUTVRa0ZOUkVFeVowRk5SMVZEQ2sxUlJIbEdkbGd6TVhOd0syOXNhV1k1TW01eUwzYzFNa3RNYlhSUWIxZFdTSGgwUjJWaGVIVk9RWFJSTjBSR1pIUlRSSFpZUjJWdVZWUXJabkZCVEc0S1FXbHJRMDFFTDJoaWQwNTFVV3c1YTNJeWJteGFPVFpIYkZRelpVMURiRGM0YnpCRU1sRkxhbmd5Ukc4eFNrTTJiSEZITW1oRVRUWXhSa2MyV201NE53cFVOVlpYWmxFOVBRb3RMUzB0TFVWT1JDQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENnPT0iLCJzaWciOiJUVVZaUTBsUlJFSm1kbmRGUlVRNWIxVnlkbFJxV2pkalNuSmFhMFV3TnpOSmNVYzRWamh5TlVreGQwMVpNMWxCU25kSmFFRktLMVZZU1VObVNUVnljemRLYkRKdlJrVkJTemhMUnl0aGMxSlhVVUVyV210U1FURnFWekpzVlZsUCJ9LHsicHVibGljS2V5IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZha05EUVdsbFowRjNTVUpCWjBsVldtczJRakZETnpSNlRFcExMME5uUzAxaE5XcDJWa1paT0hkTmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFRjZUV3BSTTFkb1kwNU5ha2w0VFZSQk5VMUVRVEJOYWxFelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZvWmxaRWVrRmhlbGMzVERGNU9EQTVNV05sU21aYVRtTXpWbFZOWXpWWlFUSk1ZM29LWmpSM2FFcFBWMnhEY25kTFdUTkhRekpOVWl0RVZsSnphVkZyTUdFMFZFSlZiSFZPYXpKTmEzWk5kemh0T1V0SWVqWlBRMEZWV1hkblowWkRUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZ3YUVKMUNrbEtOMEZtZW5waUwxWlNSa3RZY0hGbloxRTNRbWRCZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB4Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSU1FVmxkMEkxUVVoalFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRqTTBRS2EyZEJRVUpCVFVGVFJFSkhRV2xGUVN0WWJ6bEdkaXR1WkdOWk1rOXNOV0l5VFRoRlNIaFRNMGhvYjBKaVNqWlNRakpVWTNZdlRFMDBlVkZEU1ZGRVpBcHNjVzVTYkVvNVZYQlZOR3BPZVdoaFFsTkVaMFIwZURjck9FNVJNVWR1TnpFMGMyUllVV0Z2Um1wQlMwSm5aM0ZvYTJwUFVGRlJSRUYzVG5CQlJFSnRDa0ZxUlVGdWJYZFdaVkYxUlhOeFoxVXhTSEpLVGtKVlpIQjFOWFUyWjNkWFJIUTFUbFZHT1hwWmFYUjVhRlZOVDBkV1MxZFFSbEp6TVhwUmVUSTJiV2NLUm1neGMwRnFSVUYzVmpGVFpsZG1OeTlCU1RaWVFXNVlhR00wVmtKWVFpOUpUWGRWUW5KbGNsQnhZVXBLY1ZFM1JuUXhka0pDYmk5cVJGSlphWE56UndwMWNVbHlXVUZYU3dvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSIsInNpZyI6IlRVVlZRMGxDVlRKV2IwVTFXRzlUT0RCRlZqaHFUekJTZGxGaFZHMWxRa1ZMUjBWMFZVRkphMkprUWxSc1dXdDFRV2xGUVc0MmRrOXpWRGw2UlZwelJ6aGlXVE52UlRVMFdVaGxMelZCY0VNdldUVnBUVkJZZGpoaGNEQjJSVkU5In1dfSwiaGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjQ3MjJlOTQ1NWY0MGIzOWI4MmI2ZTIxMTZkZjliMWE1OWE4MGVjYjNkMTNlOTNkZDNkNTY3ODA2MDJiZTE2MzAifSwicGF5bG9hZEhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19fX0=', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCIBU2VoE5XoS80EV8jO0RvQaTmeBEKGEtUAIkbdBTlYkuAiEAn6vOsT9zEZsG8bY3oE54YHe/5ApC/Y5iMPXv8ap0vEQ=', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - there are more signatures in the canonicalized tlog +// body than in the DSSE envelope. +const invalidDSSETooManySigsInTLogBody = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIIC0TCCAlagAwIBAgIUOBERpxhVuZTd8XrNJheYDQ9iszAwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNjA1MTc1MjU0WhcNMjMwNjA1MTgwMjU0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExoIQxR/RxXl20dAM7pcKlC5fEwRexmHMCzsXaPpUpror/AQlst/WGcvHkbtGXciBWVkrnxnHo1I5O+73FxBrRaOCAXUwggFxMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUBhcYgqIg8VwrdSF1kVTOtPPFNSkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMC4GCisGAQQBg78wAQgEIAweaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGIjLDD8QAABAMARzBFAiAU40oVa45EOf1Miq1b+PEFFG8a/51N/ovrHcy74w9elgIhAJ0JMviR309CWzosD9k/iq9s+Ij3xjNdru4gmsf+ejV2MAoGCCqGSM49BAMDA2kAMGYCMQDK7j57m6kiLX6b/0akP4SLewDSKrpf7Nx+PdbeAe95TyQGtZJC+Z+3jTIp9zosxpECMQDqPIQLDyokUecisWPVJ2GUxsT0yw1hp3LYu63sdGxUSV68ms7QvWUKkfaBRGq+sNk=', + }, + ], + }, + tlogEntries: [ + { + logIndex: '22797666', + logId: { + keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=', + }, + kindVersion: { + kind: 'dsse', + version: '0.0.1', + }, + integratedTime: '1685987575', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIHhAfEwtfkiRK/XD1EbjArDzf9svz75oqUqfT6Mha6PHAiBE+ibnkU7x/AgKMshVT23+DLc3+OtTozW5eulQKrBQnw==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZjRjMTc1ZTVlZjQ2YjEyMzc4NzQ3MDZkYjFhYzE1YmY5ZGYzYTg5MGJlNmY1MmEyNzY0Y2QyZGFiMzJjZWQwNyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVZQ0lRRHFLRHQ2MTk3dnFjYkM3Rys0YkF6NWkrYitnSUVIRjdiMG1uWkJJejZvMmdJaEFPNG84WFdBZFdZRGUwRjZOTHVpK3hLSVA3a2hvQVUzNDZnSmYwUzNxVFpvIiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNd1ZFTkRRV3hoWjBGM1NVSkJaMGxWVDBKRlVuQjRhRloxV2xSa09GaHlUa3BvWlZsRVVUbHBjM3BCZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNXFRVEZOVkdNeFRXcFZNRmRvWTA1TmFrMTNUbXBCTVUxVVozZE5hbFV3VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjRiMGxSZUZJdlVuaFliREl3WkVGTk4zQmpTMnhETldaRmQxSmxlRzFJVFVONmMxZ0tZVkJ3VlhCeWIzSXZRVkZzYzNRdlYwZGpka2hyWW5SSFdHTnBRbGRXYTNKdWVHNUliekZKTlU4ck56TkdlRUp5VW1GUFEwRllWWGRuWjBaNFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVkNhR05aQ21keFNXYzRWbmR5WkZOR01XdFdWRTkwVUZCR1RsTnJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMGgzV1VSV1VqQlNRVkZJTDBKQ1ZYZEZORVZTV1c1S2NGbFhOVUZhUjFadldWY3hiR05wTldwaU1qQjNURUZaUzB0M1dVSkNRVWRFZG5wQlFncEJVVkZsWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREo0ZGxveWJIVk1NamxvWkZoU2IwMURORWREYVhOSFFWRlJRbWMzT0hkQlVXZEZDa2xCZDJWaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW5oMldqSnNkVXd5T1doa1dGSnZUVWxIUzBKbmIzSkNaMFZGUVdSYU5VRm5VVU1LUWtoM1JXVm5RalJCU0ZsQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpWQkxNeTlvTkhCNVowTTRjRGR2TkVGQlFVZEpha3hFUkFvNFVVRkJRa0ZOUVZKNlFrWkJhVUZWTkRCdlZtRTBOVVZQWmpGTmFYRXhZaXRRUlVaR1J6aGhMelV4VGk5dmRuSklZM2szTkhjNVpXeG5TV2hCU2pCS0NrMTJhVkl6TURsRFYzcHZjMFE1YXk5cGNUbHpLMGxxTTNocVRtUnlkVFJuYlhObUsyVnFWakpOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpyUVUxSFdVTUtUVkZFU3pkcU5UZHRObXRwVEZnMllpOHdZV3RRTkZOTVpYZEVVMHR5Y0dZM1RuZ3JVR1JpWlVGbE9UVlVlVkZIZEZwS1F5dGFLek5xVkVsd09YcHZjd3A0Y0VWRFRWRkVjVkJKVVV4RWVXOXJWV1ZqYVhOWFVGWktNa2RWZUhOVU1IbDNNV2h3TTB4WmRUWXpjMlJIZUZWVFZqWTRiWE0zVVhaWFZVdHJabUZDQ2xKSGNTdHpUbXM5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn0seyJzaWduYXR1cmUiOiJNRVlDSVFEcUtEdDYxOTd2cWNiQzdHKzRiQXo1aStiK2dJRUhGN2IwbW5aQkl6Nm8yZ0loQU80bzhYV0FkV1lEZTBGNk5MdWkreEtJUDdraG9BVTM0NmdKZjBTM3FUWm8iLCJ2ZXJpZmllciI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU13VkVORFFXeGhaMEYzU1VKQlowbFZUMEpGVW5CNGFGWjFXbFJrT0ZoeVRrcG9aVmxFVVRscGMzcEJkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA1cVFURk5WR014VFdwVk1GZG9ZMDVOYWsxM1RtcEJNVTFVWjNkTmFsVXdWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWNGIwbFJlRkl2VW5oWWJESXdaRUZOTjNCalMyeEROV1pGZDFKbGVHMUlUVU42YzFnS1lWQndWWEJ5YjNJdlFWRnNjM1F2VjBkamRraHJZblJIV0dOcFFsZFdhM0p1ZUc1SWJ6RkpOVThyTnpOR2VFSnlVbUZQUTBGWVZYZG5aMFo0VFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWQ2FHTlpDbWR4U1djNFZuZHlaRk5HTVd0V1ZFOTBVRkJHVGxOcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxRE5FZERhWE5IUVZGUlFtYzNPSGRCVVdkRkNrbEJkMlZoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbmgyV2pKc2RVd3lPV2hrV0ZKdlRVbEhTMEpuYjNKQ1owVkZRV1JhTlVGblVVTUtRa2gzUldWblFqUkJTRmxCTTFRd2QyRnpZa2hGVkVwcVIxSTBZMjFYWXpOQmNVcExXSEpxWlZCTE15OW9OSEI1WjBNNGNEZHZORUZCUVVkSmFreEVSQW80VVVGQlFrRk5RVko2UWtaQmFVRlZOREJ2Vm1FME5VVlBaakZOYVhFeFlpdFFSVVpHUnpoaEx6VXhUaTl2ZG5KSVkzazNOSGM1Wld4blNXaEJTakJLQ2sxMmFWSXpNRGxEVjNwdmMwUTVheTlwY1RsekswbHFNM2hxVG1SeWRUUm5iWE5tSzJWcVZqSk5RVzlIUTBOeFIxTk5ORGxDUVUxRVFUSnJRVTFIV1VNS1RWRkVTemRxTlRkdE5tdHBURmcyWWk4d1lXdFFORk5NWlhkRVUwdHljR1kzVG5nclVHUmlaVUZsT1RWVWVWRkhkRnBLUXl0YUt6TnFWRWx3T1hwdmN3cDRjRVZEVFZGRWNWQkpVVXhFZVc5clZXVmphWE5YVUZaS01rZFZlSE5VTUhsM01XaHdNMHhaZFRZemMyUkhlRlZUVmpZNGJYTTNVWFpYVlV0clptRkNDbEpIY1N0elRtczlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifV19fQo=', + }, + ], + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEYCIQDqKDt6197vqcbC7G+4bAz5i+b+gIEHF7b0mnZBIz6o2gIhAO4o8XWAdWYDe0F6NLui+xKIP7khoAU346gJf0S3qTZo', + keyid: '', + }, + ], + }, +}; + +// Invalid DSSE bundle - the kind/version of the tlog entry is not supported by +// the library. +const invalidUnsupportedTLogVersion = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICojCCAiegAwIBAgIUBnNi59veiDghraXiJVN+oNmEGf8wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MjI0MzAyWhcNMjIxMTA5MjI1MzAyWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvcEVI8KQl14Eic7wnD39CHWTHTFY4Eh/fXceq4jhtd9ZHi4blRV1fkfHb4PzDGLllAQ1oPco065aK9S5J4tTTKOCAUYwggFCMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUY9RRhJpR6bXYlL3nBy5Ji4l7HpkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEXo+iwwAABAMASDBGAiEAujRHi0pvUEM4h1HPmQyOgf3mNaQI5dWmjZrZmPV9OKQCIQCwOqskd/Q8v04/MI6uCoTM/sm4AKq2Le6cOwh+a7iepzAKBggqhkjOPQQDAwNpADBmAjEAz02yo3mVgSa/VWg2H5Vcdz9oZ+a2j2FwyuFEL69Px61KH5+f9YJE+UEi2NZiICVHAjEA6BkCYOwYWCTqLKy2cD0m9gz66LmDzqavhs/774KhU9+UvQIofjt9FdMrPK7qY0Fw', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6800673', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.1' }, + integratedTime: '1668034354', + inclusionPromise: { + signedEntryTimestamp: + 'MEYCIQCoePeENIkDVUK7G8P4IKDfiIBKL+9GAjBN+quNKZoSxQIhAIrfJBZVqA5ifljENdhxt953fQcsacQqeO9/aNX5Tfw8', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJkOWJiYTc3ZDJjNDAyYjBhZjQxMjNiYzNkYWJjYTQ5NGEwMjkyNDU0MDA5ZTYwZjQzZDcwNzdhOWY3MGQ2ZGE0In0sInBheWxvYWRIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNjhlNjU2YjI1MWU2N2U4MzU4YmVmODQ4M2FiMGQ1MWM2NjE5ZjNlN2ExYTlmMGU3NTgzOGQ0MWZmMzY4ZjcyOCJ9fSwicHVibGljS2V5IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZha05EUVdsbFowRjNTVUpCWjBsVlFtNU9hVFU1ZG1WcFJHZG9jbUZZYVVwV1RpdHZUbTFGUjJZNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTmFra3dUWHBCZVZkb1kwNU5ha2w0VFZSQk5VMXFTVEZOZWtGNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVYyWTBWV1NUaExVV3d4TkVWcFl6ZDNia1F6T1VOSVYxUklWRVpaTkVWb0wyWllZMlVLY1RScWFIUmtPVnBJYVRSaWJGSldNV1pyWmtoaU5GQjZSRWRNYkd4QlVURnZVR052TURZMVlVczVVelZLTkhSVVZFdFBRMEZWV1hkblowWkRUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZaT1ZKU0NtaEtjRkkyWWxoWmJFd3pia0o1TlVwcE5HdzNTSEJyZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB4Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSU1FVmxkMEkxUVVoalFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmh2SzJrS2QzZEJRVUpCVFVGVFJFSkhRV2xGUVhWcVVraHBNSEIyVlVWTk5HZ3hTRkJ0VVhsUFoyWXpiVTVoVVVrMVpGZHRhbHB5V20xUVZqbFBTMUZEU1ZGRGR3cFBjWE5yWkM5Uk9IWXdOQzlOU1RaMVEyOVVUUzl6YlRSQlMzRXlUR1UyWTA5M2FDdGhOMmxsY0hwQlMwSm5aM0ZvYTJwUFVGRlJSRUYzVG5CQlJFSnRDa0ZxUlVGNk1ESjViek50Vm1kVFlTOVdWMmN5U0RWV1kyUjZPVzlhSzJFeWFqSkdkM2wxUmtWTU5qbFFlRFl4UzBnMUsyWTVXVXBGSzFWRmFUSk9XbWtLU1VOV1NFRnFSVUUyUW10RFdVOTNXVmREVkhGTVMza3lZMFF3YlRsbmVqWTJURzFFZW5GaGRtaHpMemMzTkV0b1ZUa3JWWFpSU1c5bWFuUTVSbVJOY2dwUVN6ZHhXVEJHZHdvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEQCIEVcwKdqe8Qs6b4na4sdXctBv1q1Iww1aOj9rLO0xlGEAiAVudyhPn3rEAvhhxqqfVJSU65xNL5b7F9xMYVyNGbXbg==', + keyid: '', + }, + ], + }, +}; + +// The TLog entry is kind=hashedrekord, but this bundle is NOT a message signature bundle. +const invalidHashedrekordTLogEntryForDSSE = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUVHwehOtGn4KSD1H8RI581MfbyewwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA4MjI1ODA2WhcNMjIxMTA4MjMwODA2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGg6Hjxt2UNiJ1kwwq5XQIIwMZnJfVQ3bF01uZKteMdcV/3qhCmWOecoxRqwrbYTshGg9NyXcBbve6zKwZVTLeqOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU7WpR60sCpgfu04wcsjvCFxt0fMkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWXcR8AAABAMARjBEAiBRTrGE5Y1EnYniaJB+nsv89VaYx3QZjocEin3r91wfkAIgMss+fssu5SLQkn7WDTKXgow7SxbHYSZj3ykxArVnuzEwCgYIKoZIzj0EAwMDaAAwZQIxAPjSGddLIvyUMGIkZ+u6JhE9p1Njt3dEtwYkMxfnEV2k7MH1BVmxg9PsJjqycfi+eAIwDaKn2CdOxKsxcgYNi4HviEnZqxmeDyo2YFItzpHfIMQmcRSl91UeOSC8+PuGgwMK', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + inclusionProof: undefined, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + dsseEnvelope: { + payload: 'aGVsbG8sIHdvcmxkIQ==', + payloadType: 'text/plain', + signatures: [ + { + sig: 'MEUCICUhAVewfwKlk5fVzpRDUPhEw9O8I2pxC5nTnPfYDBsOAiEA0ZFqs99QgR9mAtFMWtWrOjmUC47zgaoolKIoLH/OwdM=', + keyid: '', + }, + ], + }, +}; + +export default { + // publicKey, + valid: { + withSigningCert: validBundleWithSigningCert, + // withPublicKey: validBundleWithPublicKey, + // withNoTLogEntries: validBundleWithNoTLogEntries, + withDSSETLogEntry: validBundleWithDSSETLogEntry, + }, + invalid: { + badSignatureTLogIntoto: invalidBadSignatureTLogIntoto, + badSignatureTLogDSSE: invalidBadSignatureTLogDSSE, + // noSignature: invalidNoSignature, + // tlogSETMismatch: invalidSET, + // tlogKindVersionMissing: invalidMissingKindVersion, + // tlogLogIDMissing: invalidMissingLogId, + tlogVersionMismatch: invalidTLogVersionMismatch, + tlogIntotoIncorrectSigInBody: invalidIntotoIncorrectSigInTLogBody, + tlogDSSEIncorrectSigInBody: invalidDSSEIncorrectSigInTLogBody, + tlogIntotoTooManySigsInBody: invalidIntotoTooManySigsInTLogBody, + tlogDSSETooManySigsInBody: invalidDSSETooManySigsInTLogBody, + // tlogUnsupportedVersion: invalidUnsupportedTLogVersion, + // tlogIncorrectKind: invalidHashedrekordTLogEntryForDSSE, + }, +}; diff --git a/packages/verify/src/__tests__/__fixtures__/bundles/v01/index.ts b/packages/verify/src/__tests__/__fixtures__/bundles/v01/index.ts new file mode 100644 index 00000000..f89a7305 --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/bundles/v01/index.ts @@ -0,0 +1,4 @@ +import dsse from './dsse'; +import signature from './signature'; + +export default { dsse, signature }; diff --git a/packages/verify/src/__tests__/__fixtures__/bundles/v01/signature.ts b/packages/verify/src/__tests__/__fixtures__/bundles/v01/signature.ts new file mode 100644 index 00000000..fc5739a9 --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/bundles/v01/signature.ts @@ -0,0 +1,570 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +/////////////////////////////////////////////////////////////////////////////// +// VALID BUNDLES +/////////////////////////////////////////////////////////////////////////////// + +// Valid messageSignature bundle signed with a Fulcio signing certificate +const validBundleWithSigningCert = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + inclusionProof: undefined, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; + +// Valid messageSignature bundle signed with a public key +const validBundleWithPublicKey = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + publicKey: { + hint: '9a76331edc1cfd3933040996615b1c06adbe6f9b4f11df4106dcceb66e3bdb1b', + }, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, +}; + +// Public key material for verifying the above bundle +const publicKey = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfd +tQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWg== +-----END PUBLIC KEY-----`; + +/////////////////////////////////////////////////////////////////////////////// +// INVALID BUNDLES +/////////////////////////////////////////////////////////////////////////////// + +// Invalid messageSignature bundle - expired certificate +// This bundle has a valid signature (the public key in the signing +// certificate properly verifies the signature). Also, the SET is valid +// (this was a real entry added to Rekor. However, the timestamp of the +// Rekor entry is outside the validity period of the certificate. +const invalidExpiredCert = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICnzCCAiWgAwIBAgIUL5RT/0zJNjb8IcZZjqaMM+nbFN8wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMDMwMTkwMTM2WhcNMjIxMDMwMTkxMTM2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIr5XHtIa1AmprgTYwtjl6WaAI8GLVXoUk5iRsr+aw0z2LVBEGaiWhpcAntxdN6h4YfQskDpKjmknjXfGy8/oTaOCAUQwggFAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUKeKh9ygNk/Ev2HvsYhBEeF15GU4wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUACGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3IAAAGEKkVPFQAABAMARjBEAiA2T7br2/r4Ox3zXlYoUxKbPQIxzOLHV8ocmp1SOSbQOgIgZ1G2biiCl/V0w0FuusoT7Ojw6lJUaLqipRK1qpvrPfYwCgYIKoZIzj0EAwMDaAAwZQIxAOlgn/9dfnuXzvp7pOg5PBwlIxvduDtDyQkRB2EcpQs1p3Qz9dPZ9e9U1Lb/X8t4ywIwBicA0HL2cs/Hj0WZx2GjQFlY/VA5lWsmuz6iUOFyGQ9ESgJJiH/YzfOCfC+NVZdl', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6175802', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNYejc5V2JoWGgzYnlxVVhtZExwbTVKUXV4RmM1MU05Um05ODFkTEpFN0hBSWhBS0xLUk9Sc0dJWDVjWFRVMmp2VTJ1eUFOSmZlVUVHaHhYNU5SNlpLNDY4bSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnVla05EUVdsWFowRjNTVUpCWjBsVlREVlNWQzh3ZWtwT2FtSTRTV05hV21weFlVMU5LMjVpUms0NGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFFVFhkTlZHdDNUVlJOTWxkb1kwNU5ha2w0VFVSTmQwMVVhM2hOVkUweVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZKY2pWWVNIUkpZVEZCYlhCeVoxUlpkM1JxYkRaWFlVRkpPRWRNVmxodlZXczFhVklLYzNJcllYY3dlakpNVmtKRlIyRnBWMmh3WTBGdWRIaGtUalpvTkZsbVVYTnJSSEJMYW0xcmJtcFlaa2Q1T0M5dlZHRlBRMEZWVVhkblowWkJUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZMWlV0b0NqbDVaMDVyTDBWMk1raDJjMWxvUWtWbFJqRTFSMVUwZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjBwQ1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWMwVmxVVUl6UVVoVlFVTkhRMU00UTJoVEx6Sm9SakJrUm5KS05GTmpVbGRqV1hKQ1dUbDNlbXBUWW1WaE9FbG5XVEppTTBsQlFVRkhSVXRyVmxBS1JsRkJRVUpCVFVGU2FrSkZRV2xCTWxRM1luSXlMM0kwVDNnemVsaHNXVzlWZUV0aVVGRkplSHBQVEVoV09HOWpiWEF4VTA5VFlsRlBaMGxuV2pGSE1ncGlhV2xEYkM5V01IY3dSblYxYzI5VU4wOXFkelpzU2xWaFRIRnBjRkpMTVhGd2RuSlFabGwzUTJkWlNVdHZXa2w2YWpCRlFYZE5SR0ZCUVhkYVVVbDRDa0ZQYkdkdUx6bGtabTUxV0hwMmNEZHdUMmMxVUVKM2JFbDRkbVIxUkhSRWVWRnJVa0l5UldOd1VYTXhjRE5SZWpsa1VGbzVaVGxWTVV4aUwxZzRkRFFLZVhkSmQwSnBZMEV3U0V3eVkzTXZTR293VjFwNE1rZHFVVVpzV1M5V1FUVnNWM050ZFhvMmFWVlBSbmxIVVRsRlUyZEtTbWxJTDFsNlprOURaa01yVGdwV1dtUnNDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ==', + integratedTime: '1667157111', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEYKojgVMMYh0AAIXhdXWwBs3/ywXvwGJjrGbBnckwsnAiBn29rNkIj7Gr6OgcnPlY6ZHbR2mM9cq5p2d/FBAKQOuw==', + }, + inclusionProof: undefined, + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + publicKey: undefined, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEYCIQCXz79WbhXh3byqUXmdLpm5JQuxFc51M9Rm981dLJE7HAIhAKLKRORsGIX5cXTU2jvU2uyANJfeUEGhxX5NR6ZK468m', + }, + dsseEnvelope: undefined, +}; + +const invalidDuplicateTLog = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + inclusionProof: undefined, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + inclusionProof: undefined, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; +// Valid messageSignature bundle with incorrect signature +const invalidBundleBadSignature = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + publicKey: { + hint: '9a76331edc1cfd3933040996615b1c06adbe6f9b4f11df4106dcceb66e3bdb1b', + }, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: 'DEADBEEF', + }, +}; + +// Invalid messageSignature bundle - integratedTime altered, invalidating SET +const invalidSET = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1', // Altered value + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + inclusionProof: undefined, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; + +// Invalid messageSignature bundle - missing SET +const invalidMissingSET = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: {}, // Missing SET + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, +}; + +// Invalid messageSignature bundle - version in tlogEntry don't match canonicalizedBody +const invalidTLogVersionMismatch = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.X' }, // Altered value + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, +}; + +// Invalid DSSE bundle - the signature in the canonicalized tlog body doesn't +// match the signature in the DSSE envelope. +const invalidIncorrectSigInTLogBody = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + tlogEntries: [ + { + logIndex: '6757613', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957815', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIAYvbx7NcxZ2otyk7pC5e95d4mVPq69Eq5qZQkYiis1pAiEAjJRarlKyf7QLnuAosWpYR9G2CM08EGZ3JQJ0+Pw0Cqo=', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUNTVzlWWDI4VGw5RnlKM3ZBY2xaMUc0RFQrcXh5aVVlaThEM2tBZU5KYjR3SWdmVXdEQ0NYdkdQbCtLYWQzSUVjdEYvSFlKRUdJKzRkVDlGYXlPVzlxc0xRPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlZERjRZV0o1VkZWdWJWQkxOMUpvZGxWTllrOUhTRmhTVmsxWmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUbXBWTUZkb1kwNU5ha2w0VFZSQk5VMUVSVEJPYWxVd1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZWUkRGVFVHOU1SQ3Q0VlhVellVNHdUV0l4WlVOcVVrWkRTMGhWWTJ0Vk1WRlBWV29LYnpSQlRVUTBlUzlCTmxSclpHa3ZXbmRPZWxkeGR6aDBhM2h3ZVZBM2MwNXlieTl5UlhOR1UwVlRSMjlwYkZZemQzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZvTVRkckNtOTZVUzlVZFU4dlVVTXJkakpsWldGMFIwRTNURVpyZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuYURBS2NrRkJRVUpCVFVGU2VrSkdRV2xCTDB3d1NHWlROV2hUTVN0RFpFUk9URmw1Y21adGFIaFRXa3d6UVRadU9HNVBlVEF3WkVKdmR5dGlVVWxvUVV0UU5ncHBNREZsVEdaU2VpdExhbVZHVm5KblpsSlBTRzlMVEc5NlpHTlhlWFk1YzFjclZrWk1VV0p4VFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUkhOSFZXNHJhMHRVV1hkclVuZHdSM2MzTVZFcmJuZGxkMnRxSzJOaFpVRmlNMUZYSzJSa2JqUTJSVFF4ZDBORmEya3ZXVlIyTUZWek0yaHFVRW9LVjJkRlEwMUNNazVYVVdkdmNXRkRVVmg0TjFwUk4xZFRZelJuVEZOc1dERnNhM0ZyVGtsbVRXdHBWVzFpZEhaMVZXRllWRkZGWTBkeE1FUnROemh2ZWdwTlNIbEhWbEU5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, +}; + +const invalidIncorrectDigestInTLogBody = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: { + signedEntryTimestamp: + 'MEUCIFUNcHgHB318gCNJR0/CH4E0daODbnfePyzKCDqrt3twAiEA9N+ZObaLwVJwvOtPgkfoBa5NzjTH0eC06YBlOyZlMiY=', + }, + inclusionProof: undefined, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJERUFEQkVFRiJ9fSwic2lnbmF0dXJlIjp7ImNvbnRlbnQiOiJNRVFDSUhzNWFVdWxxMUhwUitmd21TS3BMay9vQXdxNU85Q0RORkhoWkFLZkc1R21BaUJ3Y1ZuZjJvYnpzQ0dWbGYwQUl2YnZIcjIxTlh0N3RwTEJsNCtCcmg2T0tBPT0iLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU52UkVORFFXbGhaMEYzU1VKQlowbFZaWFpoWlN0dVRGRTRiV2MyVDNsUFFqUXpUVXRLTVRCR01rTkZkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUVk5SRVY2VFhwQk5WZG9ZMDVOYWtsNFRWUkJOVTFFUlRCTmVrRTFWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVVNVJHSlpRa2xOVVV4MFYySTJTalZuZEV3Mk9XcG5VbmQzUldaa2RGRjBTM1oyUnpRS0syOHpXbnBzVDNKdlNuQnNjRmhoVm1kR05uZENSRzlpS3l0eVRrYzVMMEY2VTJGQ2JVRndhMFYzU1RVeVdFSnFWM0ZQUTBGVlZYZG5aMFpDVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWV1NVbEdDbU13T0hvMmRWWTVXVGsyVXl0Mk5XOUVZbUp0U0VWWmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwdENaMjl5UW1kRlJVRmtXalZCWjFGRENrSklkMFZsWjBJMFFVaFpRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkblZVY0tVWGRCUVVKQlRVRlNla0pHUVdsRlFXeExlV05OUWtNeWNTdFJUU3R0WTNRMk1GSk9SVTU0Y0ZWU1NHVnpOblpuVDBKWFpIZzNNVmhqV0dkRFNVRjBiZ3BOZW5jdlkwSjNOV2d3YUhKWlNqaGlNVkJLYW05NGJqTnJNVTR5VkdSbmIyWnhkazFvWWxOVVRVRnZSME5EY1VkVFRUUTVRa0ZOUkVFeVowRk5SMVZEQ2sxUlF6SkxURVpaVTJsRUx5dFRNVmRGYzNsbU9XTjZaalV5ZHl0Rk5UYzNTR2szTjNJNGNFZFZUVEZ5VVM5Q2VtY3hZVWQyVVhNd0wydEJaek5UTDBvS1UwUm5RMDFGWkU0MVpFbFRNSFJTYlRGVFQwMWlUMFpqVnlzeGVYcFNLMDlwUTFaS04wUldSbmRWWkVrelJDODNSVko0ZEU0NVpTOU1TaloxWVZKdVVnb3ZVMkZ1Y25jOVBRb3RMUzB0TFVWT1JDQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENnPT0ifX19fQo=', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; + +// The TLog entry is kind=dsse, but this bundle is not a DSSE bundle. +const invalidDSSETLogEntryForMessageSig = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '22797666', + logId: { + keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=', + }, + kindVersion: { + kind: 'dsse', + version: '0.0.1', + }, + integratedTime: '1685987575', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIHhAfEwtfkiRK/XD1EbjArDzf9svz75oqUqfT6Mha6PHAiBE+ibnkU7x/AgKMshVT23+DLc3+OtTozW5eulQKrBQnw==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZjRjMTc1ZTVlZjQ2YjEyMzc4NzQ3MDZkYjFhYzE1YmY5ZGYzYTg5MGJlNmY1MmEyNzY0Y2QyZGFiMzJjZWQwNyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVZQ0lRRHFLRHQ2MTk3dnFjYkM3Rys0YkF6NWkrYitnSUVIRjdiMG1uWkJJejZvMmdJaEFPNG84WFdBZFdZRGUwRjZOTHVpK3hLSVA3a2hvQVUzNDZnSmYwUzNxVFpvIiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNd1ZFTkRRV3hoWjBGM1NVSkJaMGxWVDBKRlVuQjRhRloxV2xSa09GaHlUa3BvWlZsRVVUbHBjM3BCZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwTmQwNXFRVEZOVkdNeFRXcFZNRmRvWTA1TmFrMTNUbXBCTVUxVVozZE5hbFV3VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVjRiMGxSZUZJdlVuaFliREl3WkVGTk4zQmpTMnhETldaRmQxSmxlRzFJVFVONmMxZ0tZVkJ3VlhCeWIzSXZRVkZzYzNRdlYwZGpka2hyWW5SSFdHTnBRbGRXYTNKdWVHNUliekZKTlU4ck56TkdlRUp5VW1GUFEwRllWWGRuWjBaNFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVkNhR05aQ21keFNXYzRWbmR5WkZOR01XdFdWRTkwVUZCR1RsTnJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMGgzV1VSV1VqQlNRVkZJTDBKQ1ZYZEZORVZTV1c1S2NGbFhOVUZhUjFadldWY3hiR05wTldwaU1qQjNURUZaUzB0M1dVSkNRVWRFZG5wQlFncEJVVkZsWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREo0ZGxveWJIVk1NamxvWkZoU2IwMURORWREYVhOSFFWRlJRbWMzT0hkQlVXZEZDa2xCZDJWaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW5oMldqSnNkVXd5T1doa1dGSnZUVWxIUzBKbmIzSkNaMFZGUVdSYU5VRm5VVU1LUWtoM1JXVm5RalJCU0ZsQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpWQkxNeTlvTkhCNVowTTRjRGR2TkVGQlFVZEpha3hFUkFvNFVVRkJRa0ZOUVZKNlFrWkJhVUZWTkRCdlZtRTBOVVZQWmpGTmFYRXhZaXRRUlVaR1J6aGhMelV4VGk5dmRuSklZM2szTkhjNVpXeG5TV2hCU2pCS0NrMTJhVkl6TURsRFYzcHZjMFE1YXk5cGNUbHpLMGxxTTNocVRtUnlkVFJuYlhObUsyVnFWakpOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpyUVUxSFdVTUtUVkZFU3pkcU5UZHRObXRwVEZnMllpOHdZV3RRTkZOTVpYZEVVMHR5Y0dZM1RuZ3JVR1JpWlVGbE9UVlVlVkZIZEZwS1F5dGFLek5xVkVsd09YcHZjd3A0Y0VWRFRWRkVjVkJKVVV4RWVXOXJWV1ZqYVhOWFVGWktNa2RWZUhOVU1IbDNNV2h3TTB4WmRUWXpjMlJIZUZWVFZqWTRiWE0zVVhaWFZVdHJabUZDQ2xKSGNTdHpUbXM5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0=', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; + +// The TLog entry is kind=intoto, but this bundle is not a DSSE bundle. +const invalidIntotoTLogEntryForMessageSig = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '6751924', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'intoto', version: '0.0.2' }, + integratedTime: '1667948287', + inclusionPromise: { + signedEntryTimestamp: + 'MEQCIEzguFRaGzOpMw9JJGUfqSJQ11qlzpcyVCkZfZYPwpLCAiBzdU4LnjtVKYCfyoTImFh3OLFWeOKygtS47Z8fp1GYHg==', + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoidGV4dC9wbGFpbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbFhaMEYzU1VKQlowbFZWa2gzWldoUGRFZHVORXRUUkRGSU9GSkpOVGd4VFdaaWVXVjNkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUUk5ha2t4VDBSQk1sZG9ZMDVOYWtsNFRWUkJORTFxVFhkUFJFRXlWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWSFp6WklhbmgwTWxWT2FVb3hhM2QzY1RWWVVVbEpkMDFhYmtwbVZsRXpZa1l3TVhVS1drdDBaVTFrWTFZdk0zRm9RMjFYVDJWamIzaFNjWGR5WWxsVWMyaEhaemxPZVZoalFtSjJaVFo2UzNkYVZsUk1aWEZQUTBGVlVYZG5aMFpCVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVM1YzQlNDall3YzBOd1oyWjFNRFIzWTNOcWRrTkdlSFF3WmsxcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwcENaMjl5UW1kRlJVRmtXalZCWjFGRENrSkljMFZsVVVJelFVaFZRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZkWVkxSUtPRUZCUVVKQlRVRlNha0pGUVdsQ1VsUnlSMFUxV1RGRmJsbHVhV0ZLUWl0dWMzWTRPVlpoV1hnelVWcHFiMk5GYVc0emNqa3hkMlpyUVVsblRYTnpLd3BtYzNOMU5WTk1VV3R1TjFkRVZFdFlaMjkzTjFONFlraFpVMXBxTTNscmVFRnlWbTUxZWtWM1EyZFpTVXR2V2tsNmFqQkZRWGROUkdGQlFYZGFVVWw0Q2tGUWFsTkhaR1JNU1haNVZVMUhTV3RhSzNVMlNtaEZPWEF4VG1wME0yUkZkSGRaYTAxNFptNUZWakpyTjAxSU1VSldiWGhuT1ZCelNtcHhlV05tYVNzS1pVRkpkMFJoUzI0eVEyUlBlRXR6ZUdObldVNXBORWgyYVVWdVduRjRiV1ZFZVc4eVdVWkpkSHB3U0daSlRWRnRZMUpUYkRreFZXVlBVME00SzFCMVJ3cG5kMDFMQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbERWV2hCVm1WM1puZExiR3MxWmxaNmNGSkVWVkJvUlhjNVR6aEpNbkI0UXpWdVZHNVFabGxFUW5OUFFXbEZRVEJhUm5Gek9UbFJaMUk1YlVGMFJrMVhkRmR5VDJwdFZVTTBOM3BuWVc5dmJFdEpiMHhJTDA5M1pFMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNiNDkyNTljODY2MDdjMzQ2MzVkYWJiNDQzMWYwNjVlOWE3YTczNDcwNGNiNzNmMGFhMGY2YWFhMzg5NmEwNCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjY4ZTY1NmIyNTFlNjdlODM1OGJlZjg0ODNhYjBkNTFjNjYxOWYzZTdhMWE5ZjBlNzU4MzhkNDFmZjM2OGY3MjgifX19fQ==', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; + +export default { + publicKey, + artifact: Buffer.from('hello, world!'), + valid: { + withSigningCert: validBundleWithSigningCert, + withPublicKey: validBundleWithPublicKey, + }, + invalid: { + // expiredCert: invalidExpiredCert, + badSignature: invalidBundleBadSignature, + setMismatch: invalidSET, + duplicateTLog: invalidDuplicateTLog, + // setMissing: invalidMissingSET, + tlogVersionMismatch: invalidTLogVersionMismatch, + tlogIncorrectSigInBody: invalidIncorrectSigInTLogBody, + tlogIncorrectDigestInBody: invalidIncorrectDigestInTLogBody, + // tlogIncorrectKindDSSE: invalidDSSETLogEntryForMessageSig, + // tlogIncorrectKindIntoto: invalidIntotoTLogEntryForMessageSig, + }, +}; diff --git a/packages/verify/src/__tests__/__fixtures__/bundles/v02/index.ts b/packages/verify/src/__tests__/__fixtures__/bundles/v02/index.ts new file mode 100644 index 00000000..e001fbe7 --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/bundles/v02/index.ts @@ -0,0 +1,32 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import crypto from 'crypto'; +import signature from './signature'; + +export default { signature }; + +const tlogKey = crypto.createPublicKey(`-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr +kBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw== +-----END PUBLIC KEY-----`); + +const tlogKeyID = crypto + .createHash('sha256') + .update(tlogKey.export({ format: 'der', type: 'spki' })) + .digest() + .toString('hex'); + +export const tlogKeys = { [tlogKeyID]: tlogKey }; diff --git a/packages/verify/src/__tests__/__fixtures__/bundles/v02/signature.ts b/packages/verify/src/__tests__/__fixtures__/bundles/v02/signature.ts new file mode 100644 index 00000000..39279263 --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/bundles/v02/signature.ts @@ -0,0 +1,166 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +/////////////////////////////////////////////////////////////////////////////// +// VALID BUNDLES +/////////////////////////////////////////////////////////////////////////////// + +// Valid messageSignature bundle signed with a Fulcio signing certificate +const validBundleWithSigningCert = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.2', + verificationMaterial: { + x509CertificateChain: { + certificates: [ + { + rawBytes: + 'MIICoDCCAiagAwIBAgIUevae+nLQ8mg6OyOB43MKJ10F2CEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MDEzMzA5WhcNMjIxMTA5MDE0MzA5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9DbYBIMQLtWb6J5gtL69jgRwwEfdtQtKvvG4+o3ZzlOroJplpXaVgF6wBDob++rNG9/AzSaBmApkEwI52XBjWqOCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVIIFc08z6uV9Y96S+v5oDbbmHEYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEWgUGQwAABAMARzBFAiEAlKycMBC2q+QM+mct60RNENxpURHes6vgOBWdx71XcXgCIAtnMzw/cBw5h0hrYJ8b1PJjoxn3k1N2TdgofqvMhbSTMAoGCCqGSM49BAMDA2gAMGUCMQC2KLFYSiD/+S1WEsyf9czf52w+E577Hi77r8pGUM1rQ/Bzg1aGvQs0/kAg3S/JSDgCMEdN5dIS0tRm1SOMbOFcW+1yzR+OiCVJ7DVFwUdI3D/7ERxtN9e/LJ6uaRnR/Sanrw==', + }, + ], + }, + publicKey: undefined, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: undefined, + inclusionProof: { + logIndex: '2594072', + rootHash: 'kAnoYYy8iB3NjC5tE2l6pGBqY3uw3CBJ6x2cBBQXu0U=', + treeSize: '22954907', + hashes: [ + 'qEpgYkIiW7jVzbHp54MraVJQ1AE72Zvr5XSohvcdBN4=', + 'wtdXKmzwBO1Lr1bY5gOXpVUiP0OxYRRa9ZodfVYRKw8=', + 'ikD2dl7XVH3EKAPc6k21SYog5TYdwp/8DayXZ8Eedtw=', + '3oHeiTXTqKZMOpundZhKh4c6dznt7SdFj88Gog5DCYY=', + '4By9NfYQqHZOn5CusfRqIGw9/NeQr5E1nG4ICulNnUo=', + 'p3BgRy0uSg6SRAqcKt8qXUIDhhJhox1tCAIaHdT5tac=', + 'lJvUc0jjih3wNA1S7cbtw1q5HYX3JxYY5fO9ytPIKLU=', + '5vWL6hRP9EBDNAuXS3E236YUwutNv6qvIWTfcdzywFA=', + '1ODC3wToc5Hqky2sJQ2w3mBFggDWdZROOAv4MXWWLw0=', + 'QqwionvWKT5a3Kqsx1UWIYDsBIMK7H+pvKZNon1g4A4=', + '9Ckxujk8Sg094zTRpBWmwd4ZWNT7W72H/S2JPKbZiBY=', + '/gKT0/YRP2WbANUct+sWMGGQ2a9lQlNFBb/XYAhb/j8=', + 'f+eeYNJFCZRAI6IKsab+xTmMUl9g6Km2h6KUztMHpxM=', + 'P8eLjDLaNzX9cTdqiFIKYjyVJv4cNwxPBh1Ppg8eDvM=', + '7NR456rTv4HEGWxCwUOTYm7ze69yMkqG4f8MbhE43oU=', + 'Ul2YswjUyBqbJ3eka2zE0MI0QxT4ez8sCJ1Z3+vvMw8=', + 'ucRPSmGLhm/SyHL7chQ5vBEFull08HzsqtAC0TQ91tY=', + 'EiS8ntcvGnB1xcGZg9Cf3fTkV1wBcJNVtSWKIYVZqAU=', + 'Mx1LEx7szsPd62CGkL6HM+NWkOy9YwZTwukJEVgH7Cw=', + 's2Z13KVYurVY6F1AUhr8Uby4RE3RXW1XEC2tWWdzCjI=', + 'QRfYxLEHh/FwMZqWnxNNW+x3lY7o3LM86BW+z0MpMN4=', + 'J0dGjQ7V5bETi7p7eWg2ephCQ32QBLMWY5HxFcuGfR4=', + 'uFGzOQorMYmYZ2yumLpgr1tvXvZaL+tTTCqaXa7Hdds=', + 'Lksw/hm/y+1p33SaEF8/60gPvFVNkueBpDWJ1tAVcAo=', + 'o4Smg8NUiGzxKxvvvgjtH2NV82EZSBLcUDUo9IpzS0Y=', + ], + checkpoint: { + envelope: + 'rekor.sigstore.dev - 2605736670972794746\n22954907\nkAnoYYy8iB3NjC5tE2l6pGBqY3uw3CBJ6x2cBBQXu0U=\nTimestamp: 1689107716054191855\n\n— rekor.sigstore.dev wNI9ajBFAiEA8OpuifHq4iqd6ZJSRiVQbe00eTdZllaQ51fgfAVxAPkCIDC64vV4bCtkn3S8CyMaTHHWgD2E/a+nm0eFBADK/LFP\n', + }, + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, + dsseEnvelope: undefined, +}; + +// Valid messageSignature bundle signed with a public key +const validBundleWithPublicKey = { + mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.2', + verificationMaterial: { + publicKey: { + hint: '9a76331edc1cfd3933040996615b1c06adbe6f9b4f11df4106dcceb66e3bdb1b', + }, + tlogEntries: [ + { + logIndex: '6757503', + logId: { keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=' }, + kindVersion: { kind: 'hashedrekord', version: '0.0.1' }, + integratedTime: '1667957590', + inclusionPromise: undefined, + inclusionProof: { + logIndex: '2594072', + rootHash: 'kAnoYYy8iB3NjC5tE2l6pGBqY3uw3CBJ6x2cBBQXu0U=', + treeSize: '22954907', + hashes: [ + 'qEpgYkIiW7jVzbHp54MraVJQ1AE72Zvr5XSohvcdBN4=', + 'wtdXKmzwBO1Lr1bY5gOXpVUiP0OxYRRa9ZodfVYRKw8=', + 'ikD2dl7XVH3EKAPc6k21SYog5TYdwp/8DayXZ8Eedtw=', + '3oHeiTXTqKZMOpundZhKh4c6dznt7SdFj88Gog5DCYY=', + '4By9NfYQqHZOn5CusfRqIGw9/NeQr5E1nG4ICulNnUo=', + 'p3BgRy0uSg6SRAqcKt8qXUIDhhJhox1tCAIaHdT5tac=', + 'lJvUc0jjih3wNA1S7cbtw1q5HYX3JxYY5fO9ytPIKLU=', + '5vWL6hRP9EBDNAuXS3E236YUwutNv6qvIWTfcdzywFA=', + '1ODC3wToc5Hqky2sJQ2w3mBFggDWdZROOAv4MXWWLw0=', + 'QqwionvWKT5a3Kqsx1UWIYDsBIMK7H+pvKZNon1g4A4=', + '9Ckxujk8Sg094zTRpBWmwd4ZWNT7W72H/S2JPKbZiBY=', + '/gKT0/YRP2WbANUct+sWMGGQ2a9lQlNFBb/XYAhb/j8=', + 'f+eeYNJFCZRAI6IKsab+xTmMUl9g6Km2h6KUztMHpxM=', + 'P8eLjDLaNzX9cTdqiFIKYjyVJv4cNwxPBh1Ppg8eDvM=', + '7NR456rTv4HEGWxCwUOTYm7ze69yMkqG4f8MbhE43oU=', + 'Ul2YswjUyBqbJ3eka2zE0MI0QxT4ez8sCJ1Z3+vvMw8=', + 'ucRPSmGLhm/SyHL7chQ5vBEFull08HzsqtAC0TQ91tY=', + 'EiS8ntcvGnB1xcGZg9Cf3fTkV1wBcJNVtSWKIYVZqAU=', + 'Mx1LEx7szsPd62CGkL6HM+NWkOy9YwZTwukJEVgH7Cw=', + 's2Z13KVYurVY6F1AUhr8Uby4RE3RXW1XEC2tWWdzCjI=', + 'QRfYxLEHh/FwMZqWnxNNW+x3lY7o3LM86BW+z0MpMN4=', + 'J0dGjQ7V5bETi7p7eWg2ephCQ32QBLMWY5HxFcuGfR4=', + 'uFGzOQorMYmYZ2yumLpgr1tvXvZaL+tTTCqaXa7Hdds=', + 'Lksw/hm/y+1p33SaEF8/60gPvFVNkueBpDWJ1tAVcAo=', + 'o4Smg8NUiGzxKxvvvgjtH2NV82EZSBLcUDUo9IpzS0Y=', + ], + checkpoint: { + envelope: + 'rekor.sigstore.dev - 2605736670972794746\n22954907\nkAnoYYy8iB3NjC5tE2l6pGBqY3uw3CBJ6x2cBBQXu0U=\nTimestamp: 1689107716054191855\n\n— rekor.sigstore.dev wNI9ajBFAiEA8OpuifHq4iqd6ZJSRiVQbe00eTdZllaQ51fgfAVxAPkCIDC64vV4bCtkn3S8CyMaTHHWgD2E/a+nm0eFBADK/LFP\n', + }, + }, + canonicalizedBody: + 'eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2OGU2NTZiMjUxZTY3ZTgzNThiZWY4NDgzYWIwZDUxYzY2MTlmM2U3YTFhOWYwZTc1ODM4ZDQxZmYzNjhmNzI4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHM1YVV1bHExSHBSK2Z3bVNLcExrL29Bd3E1TzlDRE5GSGhaQUtmRzVHbUFpQndjVm5mMm9ienNDR1ZsZjBBSXZidkhyMjFOWHQ3dHBMQmw0K0JyaDZPS0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZSRU5EUVdsaFowRjNTVUpCWjBsVlpYWmhaU3R1VEZFNGJXYzJUM2xQUWpRelRVdEtNVEJHTWtORmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVUVRWTlJFVjZUWHBCTlZkb1kwNU5ha2w0VFZSQk5VMUVSVEJOZWtFMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVU1UkdKWlFrbE5VVXgwVjJJMlNqVm5kRXcyT1dwblVuZDNSV1prZEZGMFMzWjJSelFLSzI4elducHNUM0p2U25Cc2NGaGhWbWRHTm5kQ1JHOWlLeXR5VGtjNUwwRjZVMkZDYlVGd2EwVjNTVFV5V0VKcVYzRlBRMEZWVlhkblowWkNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXU1VsR0NtTXdPSG8yZFZZNVdUazJVeXQyTlc5RVltSnRTRVZaZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoM1dVUldVakJTUVZGSUwwSkNWWGRGTkVWU1dXNUtjRmxYTlVGYVIxWnZXVmN4YkdOcE5XcGlNakIzVEVGWlMwdDNXVUpDUVVkRWRucEJRZ3BCVVZGbFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKNGRsb3liSFZNTWpsb1pGaFNiMDFKUjB0Q1oyOXlRbWRGUlVGa1dqVkJaMUZEQ2tKSWQwVmxaMEkwUVVoWlFUTlVNSGRoYzJKSVJWUktha2RTTkdOdFYyTXpRWEZLUzFoeWFtVlFTek12YURSd2VXZERPSEEzYnpSQlFVRkhSVmRuVlVjS1VYZEJRVUpCVFVGU2VrSkdRV2xGUVd4TGVXTk5Ra015Y1N0UlRTdHRZM1EyTUZKT1JVNTRjRlZTU0dWek5uWm5UMEpYWkhnM01WaGpXR2REU1VGMGJncE5lbmN2WTBKM05XZ3dhSEpaU2poaU1WQkthbTk0YmpOck1VNHlWR1JuYjJaeGRrMW9ZbE5VVFVGdlIwTkRjVWRUVFRRNVFrRk5SRUV5WjBGTlIxVkRDazFSUXpKTFRFWlpVMmxFTHl0VE1WZEZjM2xtT1dONlpqVXlkeXRGTlRjM1NHazNOM0k0Y0VkVlRURnlVUzlDZW1jeFlVZDJVWE13TDJ0Qlp6TlRMMG9LVTBSblEwMUZaRTQxWkVsVE1IUlNiVEZUVDAxaVQwWmpWeXN4ZVhwU0swOXBRMVpLTjBSV1JuZFZaRWt6UkM4M1JWSjRkRTQ1WlM5TVNqWjFZVkp1VWdvdlUyRnVjbmM5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19', + }, + ], + timestampVerificationData: { rfc3161Timestamps: [] }, + }, + messageSignature: { + messageDigest: { + algorithm: 'SHA2_256', + digest: 'aOZWslHmfoNYvvhIOrDVHGYZ8+ehqfDnWDjUH/No9yg=', + }, + signature: + 'MEQCIHs5aUulq1HpR+fwmSKpLk/oAwq5O9CDNFHhZAKfG5GmAiBwcVnf2obzsCGVlf0AIvbvHr21NXt7tpLBl4+Brh6OKA==', + }, +}; + +export default { + artifact: Buffer.from('hello, world!'), + valid: { + withSigningCert: validBundleWithSigningCert, + withPublicKey: validBundleWithPublicKey, + }, + invalid: {}, +}; diff --git a/packages/verify/src/__tests__/__fixtures__/certs.ts b/packages/verify/src/__tests__/__fixtures__/certs.ts new file mode 100644 index 00000000..0b84bbc8 --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/certs.ts @@ -0,0 +1,102 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +export const certificates = { + root: `-----BEGIN CERTIFICATE----- + MIIBzTCCAVOgAwIBAgIUYcwp7HLy4oRYoMMwt+jUA1f6/p4wCgYIKoZIzj0EAwMw + JjETMBEGA1UECgwKZm9vYmFyLmRldjEPMA0GA1UEAwwGZm9vYmFyMB4XDTkwMDEw + MTAwMDAwMFoXDTQwMDEwMTAwMDAwMFowJjETMBEGA1UECgwKZm9vYmFyLmRldjEP + MA0GA1UEAwwGZm9vYmFyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE93Ne0YYOCj7I + JGEn/SdtPXhEQLMBoF8VQd8jzg+RtR0JB4SqRw4YiU6Ms8P+pywl5uXA4dXK65Xq + yXBAnvqoIIOD/F6rqc0oPecI2Q+qNC+WfhAPrABTe2HD/fiUm7pOo0IwQDAdBgNV + HQ4EFgQUWjVEZ39pYrDjAokrPUcF7TE3g+owDwYDVR0TAQH/BAUwAwEB/zAOBgNV + HQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwfHXFzEIfSuFpY4Np3iCQVe20 + UXDUsZd8kBfoC4nSR2tmzLXfakRE4lJrDpApZSddAjEAmEp+i93OpjdcppPVklbJ + 0d4oLqCCEiykfCaKGoPe6eT/l9Ip1Ufe3fth/Qf5ZDwS + -----END CERTIFICATE-----`, + + intermediate: `-----BEGIN CERTIFICATE----- + MIIB8jCCAXigAwIBAgIUGgXM883oo5AKvnltwUHySW/eaQQwCgYIKoZIzj0EAwMw + JjETMBEGA1UECgwKZm9vYmFyLmRldjEPMA0GA1UEAwwGZm9vYmFyMB4XDTkwMDEw + MTAwMDAwMFoXDTQwMDEwMTAwMDAwMFowMzETMBEGA1UECgwKZm9vYmFyLmRldjEc + MBoGA1UEAwwTZm9vYmFyLWludGVybWVkaWF0ZTB2MBAGByqGSM49AgEGBSuBBAAi + A2IABL7slLvHIJ7hFDckvhNxssGcKhz9uektKJxpjOJbpJRzYrurKTXqvwp+rw0H + 5La/gVPeSa52rsi4f/v3SJhlmrdnLLqFv5DHmL9tOjX8QZ64ExkZS/LD3Gnm4YND + 4grDbKNaMFgwHQYDVR0OBBYEFJEun/IAIfi7kOhDUcOk1mmr/WZrMA4GA1UdDwEB + /wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1UdJQQMMAoGCCsGAQUFBwMD + MAoGCCqGSM49BAMDA2gAMGUCMQC3LKOXUS9w1c/FHuxVCY2dMm/KbuqPbyVqFqSH + 9GqkMdc1QEfca45ETBluST16L60CMBDWMMldIHz3rpBaqqHiXu5lOEW+5HtxX3ku + HeHIOtAOwCsfuUt6xcwEtS+G7wtTog== + -----END CERTIFICATE-----`, + + leaf: `-----BEGIN CERTIFICATE----- + MIIB8TCCAXegAwIBAgIUBHcWnSa4N1K+z/dRDitfwGT6RUowCgYIKoZIzj0EAwMw + MzETMBEGA1UECgwKZm9vYmFyLmRldjEcMBoGA1UEAwwTZm9vYmFyLWludGVybWVk + aWF0ZTAeFw05MDAxMDEwMDAwMDBaFw00MDAxMDEwMDAwMDBaMAAwWTATBgcqhkjO + PQIBBggqhkjOPQMBBwNCAARSG+kx7P0C96xegjJgg81uJrJf/G+yYLRKucwP3AMP + Q1xFB+/8wdUqeTLZPI7AsmcGtvbT/Vr5GRPNT1NUSlFVo4GbMIGYMB0GA1UdDgQW + BBTXA23F2RNNOlWky1b9MQ1AX3NfQzAfBgNVHSMEGDAWgBSRLp/yACH4u5DoQ1HD + pNZpq/1mazAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwMQYD + VR0RBCowKIYRaHR0cDovL2Zvb2Jhci5kZXagEwYKKwYBBAGDvzABB6AFDANGT08w + CgYIKoZIzj0EAwMDaAAwZQIwWi7Kx/jf8O3riw+dLxK2p4+JPbH92aFrq3WozDex + iXb1ZTM3FhaFFrM15gMKWlVhAjEAig8qoM7nW0cPq0x029VvJPjm4knz7ZvmnY3d + VwmStvcPrB+2+tmxDfK1BKl1v5/Z + -----END CERTIFICATE-----`, + + // Leaf cert which was signed by another leaf certificate. + invalidleaf: `-----BEGIN CERTIFICATE----- + MIIBijCCAS+gAwIBAgIUay2QH/yn7biKcZIG3ngAAJ1UIDswCgYIKoZIzj0EAwMw + ADAeFw05MDAxMDEwMDAwMDBaFw00MDAxMDEwMDAwMDBaMAAwWTATBgcqhkjOPQIB + BggqhkjOPQMBBwNCAATQS7/83FBWZHvsR6pqC9S13E7+cXeyACaTaeZ/q7QT9KLM + Z4V3B5HIZpCF8uYbSG570bxDZPNLasDgH4YaN0rdo4GGMIGDMB0GA1UdDgQWBBTl + VbnUsc5wOUOk6dm9VKhDo4+T7TAfBgNVHSMEGDAWgBTXA23F2RNNOlWky1b9MQ1A + X3NfQzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHAYDVR0R + BBUwE4YRaHR0cDovL2Zvb2Jhci5kZXYwCgYIKoZIzj0EAwMDSQAwRgIhAK4F+zVs + ihy1Jz7xHResesgWpDn8ymbk36z5Iu0XZPx2AiEAiK9Rmcl6BqdI6UCfDeXdCPaL + X+LFBrV+0VdjR5jl17g= + -----END CERTIFICATE-----`, + + // Unauthorized intermediate cert which was signed by 'intermediate' + // above. This cert is not authorized due to the fact that the + // issuing cert has a pathlen:0 constraint. + invalidint: `-----BEGIN CERTIFICATE----- + MIICCzCCAZGgAwIBAgIUGdCThOLeJHfzGnsFYQzqrDPxQqAwCgYIKoZIzj0EAwMw + MzETMBEGA1UECgwKZm9vYmFyLmRldjEcMBoGA1UEAwwTZm9vYmFyLWludGVybWVk + aWF0ZTAeFw05MDAxMDEwMDAwMDBaFw00MDAxMDEwMDAwMDBaMDsxEzARBgNVBAoM + CmZvb2Jhci5kZXYxJDAiBgNVBAMMG2Zvb2Jhci1pbnZhbGlkLWludGVybWVkaWF0 + ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNBLv/zcUFZke+xHqmoL1LXcTv5x + d7IAJpNp5n+rtBP0osxnhXcHkchmkIXy5htIbnvRvENk80tqwOAfhho3St2jezB5 + MB0GA1UdDgQWBBTlVbnUsc5wOUOk6dm9VKhDo4+T7TAfBgNVHSMEGDAWgBSRLp/y + ACH4u5DoQ1HDpNZpq/1mazAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB + /wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAKBggqhkjOPQQDAwNoADBlAjEA/XMQ + omRjxkN7dApOGQ9rD1yF9oBNDKrWI4Pe90pkMpkn5/jPoMnqABPDkakBYJh5AjAu + yD/zS+yrv7qbzB5Jumi/T9El3fYz7mpW8INtwHg/d0BrO9Mkt/mkTjiEjeX6AoI= + -----END CERTIFICATE-----`, + + // Leaf which was issued by the invalid intermediate cert above and + // violates the pathlen:0 constraint of the true intermediate. + deepleaf: `-----BEGIN CERTIFICATE----- + MIIB2TCCAX+gAwIBAgIUdE9ALIE7nFHMkrZV51ZMmLlGFrQwCgYIKoZIzj0EAwMw + OzETMBEGA1UECgwKZm9vYmFyLmRldjEkMCIGA1UEAwwbZm9vYmFyLWludmFsaWQt + aW50ZXJtZWRpYXRlMB4XDTkwMDEwMTAwMDAwMFoXDTQwMDEwMTAwMDAwMFowADBZ + MBMGByqGSM49AgEGCCqGSM49AwEHA0IABFIb6THs/QL3rF6CMmCDzW4msl/8b7Jg + tEq5zA/cAw9DXEUH7/zB1Sp5Mtk8jsCyZwa29tP9WvkZE81PU1RKUVWjgZswgZgw + HQYDVR0OBBYEFNcDbcXZE006VaTLVv0xDUBfc19DMB8GA1UdIwQYMBaAFOVVudSx + znA5Q6Tp2b1UqEOjj5PtMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEF + BQcDAzAxBgNVHREEKjAohhFodHRwOi8vZm9vYmFyLmRldqATBgorBgEEAYO/MAEH + oAUMA0ZPTzAKBggqhkjOPQQDAwNIADBFAiEA4Mo94nfLm+Tppp4s2TT/fgc3bJnD + STIQAJ48P98hceMCIHwwDrjqaQJNUNV3BwlEMc1DYkHo2p4IZf6zXi+Aasih + -----END CERTIFICATE-----`, +}; diff --git a/packages/verify/src/__tests__/__fixtures__/trust.ts b/packages/verify/src/__tests__/__fixtures__/trust.ts new file mode 100644 index 00000000..5d6ef965 --- /dev/null +++ b/packages/verify/src/__tests__/__fixtures__/trust.ts @@ -0,0 +1,107 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { TrustedRoot } from '@sigstore/protobuf-specs'; + +const trustedRootJSON = { + mediaType: 'application/vnd.dev.sigstore.trustedroot+json;version=0.1', + tlogs: [ + { + baseUrl: 'https://tlog.sigstore.dev', + hashAlgorithm: 'SHA2_256', + publicKey: { + rawBytes: + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==', + keyDetails: 'PKIX_ECDSA_P256_SHA_256', + }, + logId: { + keyId: 'wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=', + }, + }, + ], + certificateAuthorities: [ + { + subject: { + organization: 'sigstore.dev', + commonName: 'sigstore', + }, + uri: 'https://fulcio.sigstore.dev', + certChain: { + certificates: [ + { + rawBytes: + 'MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==', + }, + ], + }, + validFor: { + start: undefined, + end: undefined, + }, + }, + { + subject: { + organization: 'sigstore.dev', + commonName: 'sigstore', + }, + uri: 'https://fulcio.sigstore.dev', + certChain: { + certificates: [ + { + rawBytes: + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ', + }, + { + rawBytes: + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=', + }, + ], + }, + validFor: { + start: '2022-04-13T20:06:15.000Z', + end: '2031-10-05T13:56:58.000Z', + }, + }, + ], + ctlogs: [ + { + baseUrl: 'https://ctfe.sigstore.dev/test', + hashAlgorithm: 'SHA2_256', + publicKey: { + rawBytes: + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==', + keyDetails: 'PKIX_ECDSA_P256_SHA_256', + }, + logId: { + keyId: 'CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=', + }, + }, + { + baseUrl: 'https://ctfe.sigstore.dev/2022', + hashAlgorithm: 'SHA2_256', + publicKey: { + rawBytes: + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==', + keyDetails: 'PKIX_ECDSA_P256_SHA_256', + }, + logId: { + keyId: '3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4=', + }, + }, + ], + timestampAuthorities: [], +}; + +export const trustedRoot = TrustedRoot.fromJSON(trustedRootJSON); diff --git a/packages/verify/src/__tests__/bundle/dsse.test.ts b/packages/verify/src/__tests__/bundle/dsse.test.ts new file mode 100644 index 00000000..bee48a6a --- /dev/null +++ b/packages/verify/src/__tests__/bundle/dsse.test.ts @@ -0,0 +1,100 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import type { Envelope } from '@sigstore/bundle'; +import { crypto as core } from '@sigstore/core'; +import crypto from 'crypto'; +import { DSSESignatureContent } from '../../bundle/dsse'; + +describe('DSSESignatureContent', () => { + const key = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }); + const payload = Buffer.from('payload'); + const payloadType = 'payloadType'; + const pae = Buffer.from( + `DSSEv1 ${payloadType.length} ${payloadType} ${payload.length} ${payload}` + ); + + const env: Envelope = { + payload, + payloadType, + signatures: [ + { + sig: crypto.sign(null, pae, key.privateKey), + keyid: '', + }, + ], + }; + + const subject = new DSSESignatureContent(env); + + describe('#compareDigest', () => { + describe('when the digest does NOT match the payload hash', () => { + it('returns false', () => { + expect(subject.compareDigest(Buffer.from(''))).toBe(false); + }); + }); + + describe('when the digest matches the payload hash', () => { + const expectedDigest = core.hash(env.payload); + + it('returns true', () => { + expect(subject.compareDigest(expectedDigest)).toBe(true); + }); + }); + }); + + describe('#compareSignature', () => { + describe('when the signature does NOT match the payload hash', () => { + it('returns false', () => { + expect(subject.compareSignature(Buffer.from(''))).toBe(false); + }); + }); + + describe('when the signature matches the payload hash', () => { + const expectedSignature = env.signatures[0].sig; + + it('returns true', () => { + expect(subject.compareSignature(expectedSignature)).toBe(true); + }); + }); + }); + + describe('#verifySignature', () => { + describe('when the signature is valid', () => { + it('returns true', () => { + expect(subject.verifySignature(key.publicKey)).toBe(true); + }); + }); + + describe('when there are no signatures', () => { + const env: Envelope = { payload, payloadType, signatures: [] }; + const subject = new DSSESignatureContent(env); + + it('returns false', () => { + expect(subject.verifySignature(key.publicKey)).toBe(false); + }); + }); + + describe('when the signature is NOT valid', () => { + const invalidKey = crypto.generateKeyPairSync('ec', { + namedCurve: 'secp256k1', + }); + + it('returns false', () => { + expect(subject.verifySignature(invalidKey.publicKey)).toBe(false); + }); + }); + }); +}); diff --git a/packages/verify/src/__tests__/bundle/index.test.ts b/packages/verify/src/__tests__/bundle/index.test.ts new file mode 100644 index 00000000..e3ed8698 --- /dev/null +++ b/packages/verify/src/__tests__/bundle/index.test.ts @@ -0,0 +1,56 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { X509Certificate } from '@sigstore/core'; +import assert from 'assert'; +import { toSignedEntity } from '../../bundle'; +import bundles from '../__fixtures__/bundles/v01'; + +describe('toSignedEntity', () => { + describe('when the bundle is a dsseEnvelope', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withSigningCert); + + it('returns a SignedEntity', () => { + const entity = toSignedEntity(bundle); + + expect(entity).toBeDefined(); + + assert(entity.key.$case === 'certificate'); + expect(entity.key.certificate).toBeInstanceOf(X509Certificate); + + expect(entity.signature).toBeDefined(); + expect(entity.tlogEntries).toHaveLength(1); + expect(entity.timestamps).toHaveLength(1); + }); + }); + + describe('when the bundle is a messageSignature', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withPublicKey); + + it('returns a SignedEntity', () => { + const entity = toSignedEntity(bundle); + + expect(entity).toBeDefined(); + + assert(entity.key.$case === 'public-key'); + expect(entity.key.hint).toBeDefined(); + + expect(entity.signature).toBeDefined(); + expect(entity.tlogEntries).toHaveLength(1); + expect(entity.timestamps).toHaveLength(1); + }); + }); +}); diff --git a/packages/verify/src/__tests__/bundle/message.test.ts b/packages/verify/src/__tests__/bundle/message.test.ts new file mode 100644 index 00000000..fcf3c157 --- /dev/null +++ b/packages/verify/src/__tests__/bundle/message.test.ts @@ -0,0 +1,80 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto as core } from '@sigstore/core'; +import { HashAlgorithm } from '@sigstore/protobuf-specs'; +import crypto from 'crypto'; +import { MessageSignatureContent } from '../../bundle/message'; + +import type { MessageSignature } from '@sigstore/bundle'; + +describe('MessageSignatureContent', () => { + const key = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }); + const message = Buffer.from('message'); + const messageDigest = core.hash(message); + + const messageSignature: MessageSignature = { + messageDigest: { digest: messageDigest, algorithm: HashAlgorithm.SHA2_256 }, + signature: crypto.sign(null, message, key.privateKey), + }; + + const subject = new MessageSignatureContent(messageSignature, message); + + describe('#compareDigest', () => { + describe('when the digest does NOT match the message hash', () => { + it('returns false', () => { + expect(subject.compareDigest(Buffer.from(''))).toBe(false); + }); + }); + + describe('when the digest matches the message hash', () => { + it('returns true', () => { + expect(subject.compareDigest(messageDigest)).toBe(true); + }); + }); + }); + + describe('#compareSignature', () => { + describe('when the signature does NOT match the message signature', () => { + it('returns false', () => { + expect(subject.compareSignature(Buffer.from(''))).toBe(false); + }); + }); + + describe('when the signature matches the message signature', () => { + it('returns true', () => { + expect(subject.compareSignature(messageSignature.signature)).toBe(true); + }); + }); + }); + + describe('#verifySignature', () => { + describe('when the signature is NOT valid', () => { + const invalidKey = crypto.generateKeyPairSync('ec', { + namedCurve: 'secp256k1', + }); + + it('returns false', () => { + expect(subject.verifySignature(invalidKey.publicKey)).toBe(false); + }); + }); + + describe('when the signature is valid', () => { + it('returns true', () => { + expect(subject.verifySignature(key.publicKey)).toBe(true); + }); + }); + }); +}); diff --git a/packages/verify/src/__tests__/index.test.ts b/packages/verify/src/__tests__/index.test.ts new file mode 100644 index 00000000..04323891 --- /dev/null +++ b/packages/verify/src/__tests__/index.test.ts @@ -0,0 +1,48 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { fromPartial } from '@total-typescript/shoehorn'; +import { + SignedEntity, + Signer, + TrustMaterial, + Verifier, + VerifierOptions, + toSignedEntity, + toTrustMaterial, +} from '..'; + +it('exports Verifier', () => { + expect(Verifier).toBeDefined(); +}); + +it('exports functions', () => { + expect(toSignedEntity).toBeInstanceOf(Function); + expect(toTrustMaterial).toBeInstanceOf(Function); +}); + +it('exports types', async () => { + const verifierOptions: VerifierOptions = fromPartial({}); + expect(verifierOptions).toBeDefined(); + + const signedEntity: SignedEntity = fromPartial({}); + expect(signedEntity).toBeDefined(); + + const signer: Signer = fromPartial({}); + expect(signer).toBeDefined(); + + const trustMaterial: TrustMaterial = fromPartial({}); + expect(trustMaterial).toBeDefined(); +}); diff --git a/packages/verify/src/__tests__/key/certificate.test.ts b/packages/verify/src/__tests__/key/certificate.test.ts new file mode 100644 index 00000000..48e49b23 --- /dev/null +++ b/packages/verify/src/__tests__/key/certificate.test.ts @@ -0,0 +1,141 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { X509Certificate } from '@sigstore/core'; +import { VerificationError } from '../../error'; +import { verifyCertificateChain } from '../../key/certificate'; +import { certificates } from '../__fixtures__/certs'; + +import type { CertAuthority } from '../../trust'; + +describe('verifyCertificateChain', () => { + const rootCert = X509Certificate.parse(certificates.root); + const intCert = X509Certificate.parse(certificates.intermediate); + const leafCert = X509Certificate.parse(certificates.leaf); + const invalidLeafCert = X509Certificate.parse(certificates.invalidleaf); + const invalidIntCert = X509Certificate.parse(certificates.invalidint); + const deepLeafCert = X509Certificate.parse(certificates.deepleaf); + + describe('when there are no matching CAs', () => { + const cas: CertAuthority[] = [ + { + certChain: [leafCert], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }, + ]; + + it('throws an error', () => { + expect(() => verifyCertificateChain(rootCert, cas)).toThrowWithCode( + VerificationError, + 'CERTIFICATE_ERROR' + ); + }); + }); + + describe('when no path contains a trusted cert', () => { + const cas: CertAuthority[] = []; + + it('throws an error', () => { + expect(() => verifyCertificateChain(rootCert, cas)).toThrowWithCode( + VerificationError, + 'CERTIFICATE_ERROR' + ); + }); + }); + + describe('when the certificate issuer cannot be located', () => { + const cas: CertAuthority[] = []; + + it('throws an error', () => { + expect(() => verifyCertificateChain(leafCert, cas)).toThrowWithCode( + VerificationError, + 'CERTIFICATE_ERROR' + ); + }); + }); + + describe('when a non-CA cert is used to sign the leaf', () => { + const cas: CertAuthority[] = [ + { + certChain: [rootCert, intCert, leafCert], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }, + ]; + + it('throws an error', () => { + expect(() => + verifyCertificateChain(invalidLeafCert, cas) + ).toThrowWithCode(VerificationError, 'CERTIFICATE_ERROR'); + }); + }); + + describe('when the pathlen constraint is violated', () => { + const cas: CertAuthority[] = [ + { + certChain: [rootCert, intCert, invalidIntCert], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }, + ]; + + it('throws an error', () => { + expect(() => verifyCertificateChain(deepLeafCert, cas)).toThrowWithCode( + VerificationError, + 'CERTIFICATE_ERROR' + ); + }); + }); + + describe('when the cert chain is valid', () => { + const cas: CertAuthority[] = [ + { + certChain: [rootCert, intCert], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }, + ]; + + it('does not throw an error', () => { + expect(() => verifyCertificateChain(leafCert, cas)).not.toThrowError(); + }); + + it('returns the trusted chain', () => { + const trustedChain = verifyCertificateChain(leafCert, cas); + expect(trustedChain).toBeDefined(); + expect(trustedChain).toHaveLength(3); + + // The first cert in the chain is the leaf cert + expect(trustedChain[0]).toEqual(leafCert); + }); + }); + + describe('when the certificate chain contains a single valid cert', () => { + const cas: CertAuthority[] = [ + { + certChain: [rootCert], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }, + ]; + + it('does not throw an error', () => { + expect(() => verifyCertificateChain(rootCert, cas)).not.toThrowError(); + }); + + it('returns the trusted chain', () => { + const trustedChain = verifyCertificateChain(rootCert, cas); + expect(trustedChain).toBeDefined(); + expect(trustedChain).toHaveLength(1); + expect(trustedChain[0]).toEqual(rootCert); + }); + }); +}); diff --git a/packages/verify/src/__tests__/key/index.test.ts b/packages/verify/src/__tests__/key/index.test.ts new file mode 100644 index 00000000..37dfa3c5 --- /dev/null +++ b/packages/verify/src/__tests__/key/index.test.ts @@ -0,0 +1,183 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { X509Certificate, crypto } from '@sigstore/core'; +import { fromPartial } from '@total-typescript/shoehorn'; +import { VerificationError } from '../../error'; +import { verifyCertificate, verifyPublicKey } from '../../key'; +import bundles from '../__fixtures__/bundles/v01'; + +import type { CertAuthority, TLogAuthority, TrustMaterial } from '../../trust'; + +describe('verifyCertificate', () => { + const bundle = bundles.signature.valid.withSigningCert; + + const bundleCert = + bundle.verificationMaterial.x509CertificateChain.certificates[0]; + const leaf = X509Certificate.parse(bundleCert.rawBytes); + + const timestamps = [ + new Date( + Number(bundle.verificationMaterial.tlogEntries[0].integratedTime) * 1000 + ), + ]; + + // Certificates for public-good Fulcio + const rootCert = + 'MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ'; + const intCert = + 'MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow='; + + // Key for public-good Fulcio ctlog + const keyBytes = Buffer.from( + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==', + 'base64' + ); + + const certAuthority: CertAuthority = { + certChain: [ + X509Certificate.parse(rootCert), + X509Certificate.parse(intCert), + ], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }; + + const ctlogAuthority: TLogAuthority = { + logID: crypto.hash(keyBytes), + publicKey: crypto.createPublicKey(keyBytes), + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }; + + describe('when there are no matching certificate authorities', () => { + const trustMaterial = fromPartial({ + certificateAuthorities: [], + ctlogs: [], + }); + + it('throws an error', () => { + expect(() => + verifyCertificate(leaf, timestamps, trustMaterial) + ).toThrowWithCode(VerificationError, 'CERTIFICATE_ERROR'); + }); + }); + + describe('when the certificate was NOT issued by a trusted CA', () => { + const incorrectCA: CertAuthority = { + certChain: [ + X509Certificate.parse( + 'MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==' + ), + ], + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }; + + const trustMaterial = fromPartial({ + certificateAuthorities: [incorrectCA], + ctlogs: [], + }); + + it('throws an error', () => { + expect(() => + verifyCertificate(leaf, timestamps, trustMaterial) + ).toThrowWithCode(VerificationError, 'CERTIFICATE_ERROR'); + }); + }); + + describe('when there are no matching ctlog authorities', () => { + const trustMaterial = fromPartial({ + certificateAuthorities: [certAuthority], + ctlogs: [], + }); + + it('throws an error', () => { + expect(() => + verifyCertificate(leaf, timestamps, trustMaterial) + ).toThrowWithCode(VerificationError, 'CERTIFICATE_ERROR'); + }); + }); + + describe('when specified timestamp falls outside the certs validity window', () => { + const unmatchedTimestamps = [new Date(0)]; + const trustMaterial = fromPartial({ + certificateAuthorities: [certAuthority], + ctlogs: [ctlogAuthority], + }); + + it('throws an error', () => { + expect(() => + verifyCertificate(leaf, unmatchedTimestamps, trustMaterial) + ).toThrowWithCode(VerificationError, 'CERTIFICATE_ERROR'); + }); + }); + + describe('when everything is valid', () => { + const trustMaterial = fromPartial({ + certificateAuthorities: [certAuthority], + ctlogs: [ctlogAuthority], + }); + + it('returns the verifier', () => { + const result = verifyCertificate(leaf, timestamps, trustMaterial); + expect(result.scts).toHaveLength(1); + expect(result.scts[0]).toEqual(ctlogAuthority.logID); + expect(result.signer).toBeDefined(); + expect(result.signer.identity).toBeDefined(); + expect(result.signer.identity?.subjectAlternativeName).toBeDefined(); + expect(result.signer.identity?.extensions.issuer).toEqual( + 'https://github.com/login/oauth' + ); + expect(result.signer.key).toBeDefined(); + }); + }); + + describe('when a public key is supplied', () => { + const bundle = bundles.signature.valid.withPublicKey; + const key = bundles.signature.publicKey; + + const hint = bundle.verificationMaterial.publicKey.hint; + const timestamps = [new Date()]; + + describe('when the public key is not valid for the timestamp', () => { + const trustMaterial = fromPartial({ + publicKey: () => ({ + publicKey: crypto.createPublicKey(key), + validFor: () => false, + }), + }); + + it('throws an error', () => { + expect(() => + verifyPublicKey(hint, timestamps, trustMaterial) + ).toThrowWithCode(VerificationError, 'PUBLIC_KEY_ERROR'); + }); + }); + + describe('when everything is valid', () => { + const trustMaterial = fromPartial({ + publicKey: () => ({ + publicKey: crypto.createPublicKey(key), + validFor: () => true, + }), + }); + + it('returns the verifier', () => { + const result = verifyPublicKey(hint, timestamps, trustMaterial); + expect(result).toBeDefined(); + expect(result.identity).toBeUndefined(); + expect(result.key.asymmetricKeyType).toEqual('ec'); + }); + }); + }); +}); diff --git a/packages/verify/src/__tests__/key/sct.test.ts b/packages/verify/src/__tests__/key/sct.test.ts new file mode 100644 index 00000000..6a00a405 --- /dev/null +++ b/packages/verify/src/__tests__/key/sct.test.ts @@ -0,0 +1,107 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { X509Certificate, crypto } from '@sigstore/core'; +import { VerificationError } from '../../error'; +import { verifySCTs } from '../../key/sct'; +import { certificates } from '../__fixtures__/certs'; + +import type { TLogAuthority } from '../../trust'; + +describe('verifySCTs', () => { + // Fulcio ctfe key + const ctfe = + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w=='; + + const ctl: TLogAuthority = { + logID: Buffer.from( + 'CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=', + 'base64' + ), + publicKey: crypto.createPublicKey(Buffer.from(ctfe, 'base64')), + validFor: { start: new Date('2000-01-01'), end: new Date('2999-01-01') }, + }; + + const logs: TLogAuthority[] = [ctl]; + + describe('when the certificate does NOT have an SCT extension', () => { + const leaf = X509Certificate.parse(certificates.leaf); + const issuer = X509Certificate.parse(certificates.intermediate); + + it('returns an empty array', () => { + expect(verifySCTs(leaf, issuer, logs)).toHaveLength(0); + }); + }); + + describe('when the certificate has an SCT extension', () => { + // Fulcio-issued certificate with an SCT extension + const leafPEM = `-----BEGIN CERTIFICATE----- +MIICoTCCAiagAwIBAgIURm9on7zDvhPmPdvRSid8Qc1W0nEwCgYIKoZIzj0EAwMw +NzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl +cm1lZGlhdGUwHhcNMjIwNzIyMjExMTUxWhcNMjIwNzIyMjEyMTUxWjAAMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAEWfKrK8Ky+duY5xEgexxh2fhS+6RWxAodzdaQ +3p75wvumEzpWXMynav3upjUqGw28+ZPnTpAYkryk/zl3pKRUEKOCAUUwggFBMA4G +A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUHOAT +bi5c3xsJdYKpMmkF/8QPVX8wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y +ZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzAB +AQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQC +BHwEegB4AHYACGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3IAAAGCJ8Ce +nAAABAMARzBFAiEAueywtShv7qINRCpAnajFJgvWrnazEdcfrO/xx/yTyFwCIE41 +5V1imhqE+aiF52Idmzr57Y5//QJgZ5E5vadkxefQMAoGCCqGSM49BAMDA2kAMGYC +MQDYQen2LUbFkSmg2mb9hXjmNL6TNp8b8xJSje72ZYhqiuika4CyQkcByHsbORky +vjICMQDgfIBIFgnkBIn0UIacFvoF6RWlg/bmkdftHVkdDS59Uv24OpwoGndgoG8w +tLtOthg= +-----END CERTIFICATE-----`; + const leaf = X509Certificate.parse(leafPEM); + + describe('when the SCTs are valid', () => { + // Fulcio intermediate certificate + const issuerPEM = `-----BEGIN CERTIFICATE----- +MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMw +KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y +MjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3Jl +LmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV7 +7LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS +0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYB +BQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjp +KFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZI +zj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJR +nZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsP +mygUY7Ii2zbdCdliiow= +-----END CERTIFICATE-----`; + const issuer = X509Certificate.parse(issuerPEM); + + it('returns the list of verified SCTs', () => { + expect(leaf.extSCT).toBeDefined(); + const results = verifySCTs(leaf, issuer, logs); + expect(results).toBeDefined(); + expect(results).toHaveLength(1); + expect(results[0]).toEqual(ctl.logID); + }); + }); + + describe('when the SCTs are invalid', () => { + const badIssuer = X509Certificate.parse(certificates.root); + + it('throws an error', () => { + expect(() => verifySCTs(leaf, badIssuer, logs)).toThrowWithCode( + VerificationError, + 'CERTIFICATE_ERROR' + ); + }); + }); + }); +}); diff --git a/packages/verify/src/__tests__/timestamp/checkpoint.test.ts b/packages/verify/src/__tests__/timestamp/checkpoint.test.ts new file mode 100644 index 00000000..c4e24d50 --- /dev/null +++ b/packages/verify/src/__tests__/timestamp/checkpoint.test.ts @@ -0,0 +1,237 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto } from '@sigstore/core'; +import { fromPartial } from '@total-typescript/shoehorn'; +import { VerificationError } from '../../error'; +import { verifyCheckpoint } from '../../timestamp/checkpoint'; + +import type { TLogEntryWithInclusionProof } from '@sigstore/bundle'; +import type { TLogAuthority } from '../../trust'; + +describe('verifyCheckpoint', () => { + const keyBytes = Buffer.from( + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==', + 'base64' + ); + const keyID = crypto.hash(keyBytes); + + const tlogInstance: TLogAuthority = { + publicKey: crypto.createPublicKey(keyBytes), + logID: keyID, + validFor: { start: new Date('2000-01-01'), end: new Date('2100-01-01') }, + }; + + const tlogs = [tlogInstance]; + + const checkpoint = + 'rekor.sigstore.dev - 2605736670972794746\n21428036\nrxnoKyFZlJ7/R6bMh/d3lcqwKqAy5CL1LcNBJP17kgQ=\nTimestamp: 1688058656037355364\n\n— rekor.sigstore.dev wNI9ajBFAiEAuDk7uu5Ae8Own/MjhSZNuVzbLuYH2jBMxbSA0WaNDNACIDV4reKpYiOpkwtvazCClnpUuduF2o/th2xR3gRZAUU4\n'; + + const inclusionProof: TLogEntryWithInclusionProof['inclusionProof'] = + fromPartial({ + checkpoint: { envelope: checkpoint }, + rootHash: Buffer.from( + 'rxnoKyFZlJ7/R6bMh/d3lcqwKqAy5CL1LcNBJP17kgQ=', + 'base64' + ), + }); + + const entry: TLogEntryWithInclusionProof = fromPartial({ + inclusionProof: inclusionProof, + integratedTime: '1688058655', + }); + + describe('when the entry has a valid checkpoint', () => { + it('does NOT throw an error', () => { + expect(verifyCheckpoint(entry, tlogs)).toBeUndefined(); + }); + }); + + describe('when the checkpoint has no separator', () => { + const entryWithInvalidCheckpoint: TLogEntryWithInclusionProof = fromPartial( + { + inclusionProof: { + checkpoint: { envelope: 'rekor.sigstore.dev - 2605736670972794746' }, + }, + } + ); + + it('throws a VerificationError', () => { + expect(() => + verifyCheckpoint(entryWithInvalidCheckpoint, tlogs) + ).toThrowWithCode(VerificationError, 'TLOG_INCLUSION_PROOF_ERROR'); + }); + }); + + describe('when the checkpoint signature is malformed', () => { + const entryWithInvalidCheckpoint: TLogEntryWithInclusionProof = fromPartial( + { + inclusionProof: { + checkpoint: { + envelope: + 'rekor.sigstore.dev - 2605736670972794746\n\n— rekor.sigstore.dev foo\n', + }, + }, + } + ); + + it('throws a VerificationError', () => { + expect(() => + verifyCheckpoint(entryWithInvalidCheckpoint, tlogs) + ).toThrowWithCode(VerificationError, 'TLOG_INCLUSION_PROOF_ERROR'); + }); + }); + + describe('when the checkpoint has no signature', () => { + const entryWitInvalidCheckpoint: TLogEntryWithInclusionProof = fromPartial({ + inclusionProof: { + checkpoint: { + envelope: 'rekor.sigstore.dev - 2605736670972794746\n\n', + }, + }, + }); + + it('throws a VerificationError', () => { + expect(() => + verifyCheckpoint(entryWitInvalidCheckpoint, tlogs) + ).toThrowWithCode(VerificationError, 'TLOG_INCLUSION_PROOF_ERROR'); + }); + }); + + describe('when the checkpoint header is too short', () => { + const entryWithInvalidCheckpoint: TLogEntryWithInclusionProof = fromPartial( + { + inclusionProof: { + checkpoint: { + envelope: + 'rekor.sigstore.dev\n\n— rekor.sigstore.dev wNI9ajBFAiEAu\n', + }, + }, + } + ); + + it('throws a VerificationError', () => { + expect(() => + verifyCheckpoint(entryWithInvalidCheckpoint, tlogs) + ).toThrowWithCode(VerificationError, 'TLOG_INCLUSION_PROOF_ERROR'); + }); + }); + + describe('when the checkpoint origin is empty', () => { + const entryWithInvalidCheckpoint: TLogEntryWithInclusionProof = fromPartial( + { + inclusionProof: { + checkpoint: { + envelope: '\nA\nB\nC\n\n— rekor.sigstore.dev wNI9ajBFAiEAu\n', + }, + }, + } + ); + + it('throws a VerificationError', () => { + expect(() => + verifyCheckpoint(entryWithInvalidCheckpoint, tlogs) + ).toThrowWithCode(VerificationError, 'TLOG_INCLUSION_PROOF_ERROR'); + }); + }); + + describe('when the entry checkpoint has the wrong root hash', () => { + const entry: TLogEntryWithInclusionProof = fromPartial({ + inclusionProof: { ...inclusionProof, rootHash: Buffer.from('foo') }, + integratedTime: '1688058655', + }); + + it('throws an error', () => { + expect(() => verifyCheckpoint(entry, tlogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when the entry checkpoint has a bad signature', () => { + const badSignatureCheckpoint = + 'rekor.sigstore.dev - 2605736670972794746\n21428036\nrxnoKyFZlJ7/R6bMh/d3lcqwKqAy5CL1LcNBJP17kgQ=\nTimestamp: 1688058656037355364\n\n— rekor.sigstore.dev wNI9ajBFAiEAuDk7uu5Ae8Own\n'; + + const entryWithBadCheckpointSig: TLogEntryWithInclusionProof = fromPartial({ + inclusionProof: { + ...inclusionProof, + checkpoint: { envelope: badSignatureCheckpoint }, + }, + }); + + it('throws an error', () => { + expect(() => + verifyCheckpoint(entryWithBadCheckpointSig, tlogs) + ).toThrowWithCode(VerificationError, 'TLOG_INCLUSION_PROOF_ERROR'); + }); + }); + + describe('when there is no transparency log with the given key ID', () => { + const checkpointWithBadKeyHint = + 'rekor.sigstore.dev - 2605736670972794746\n21428036\nrxnoKyFZlJ7/R6bMh/d3lcqwKqAy5CL1LcNBJP17kgQ=\nTimestamp: 1688058656037355364\n\n— rekor.sigstore.dev xNI9ajBFAiEAuDk7uu5Ae8Own/MjhSZNuVzbLuYH2jBMxbSA0WaNDNACIDV4reKpYiOpkwtvazCClnpUuduF2o/th2xR3gRZAUU4\n'; + const entryWithBadLogID: TLogEntryWithInclusionProof = fromPartial({ + inclusionProof: { + ...inclusionProof, + checkpoint: { envelope: checkpointWithBadKeyHint }, + }, + }); + + it('throws an error', () => { + expect(() => verifyCheckpoint(entryWithBadLogID, tlogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when key start time is after the entry time', () => { + const invalidTLogs = [ + { + ...tlogInstance, + validFor: { + start: new Date('2099-01-01'), + end: new Date('2100-01-01'), + }, + }, + ]; + + it('throws an error', () => { + expect(() => verifyCheckpoint(entry, invalidTLogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when key is expired at the entry time', () => { + const invalidTLogs = [ + { + ...tlogInstance, + validFor: { + start: new Date('2000-01-01'), + end: new Date('2001-01-01'), + }, + }, + ]; + + it('throws an error', () => { + expect(() => verifyCheckpoint(entry, invalidTLogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/timestamp/index.test.ts b/packages/verify/src/__tests__/timestamp/index.test.ts new file mode 100644 index 00000000..e77c195d --- /dev/null +++ b/packages/verify/src/__tests__/timestamp/index.test.ts @@ -0,0 +1,82 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { crypto } from '@sigstore/core'; +import { VerificationError } from '../../error'; +import { verifyTLogTimestamp } from '../../timestamp/index'; +import bundles from '../__fixtures__/bundles/v01'; +import bundlesv02 from '../__fixtures__/bundles/v02'; + +import type { TLogAuthority } from '../../trust'; + +describe('verifyTLogTimestamp', () => { + // Actual public key for public-good Rekor + const keyBytes = Buffer.from( + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==', + 'base64' + ); + const keyID = crypto.hash(keyBytes); + + const validTLog: TLogAuthority = { + logID: keyID, + publicKey: crypto.createPublicKey(keyBytes), + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }; + + describe('when a valid bundle with inclusion promise is provided', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial!.tlogEntries[0]; + + it('does NOT throw an error', () => { + const result = verifyTLogTimestamp(tlogEntry, [validTLog]); + + expect(result.type).toEqual('transparency-log'); + expect(result.logID).toEqual(keyID); + expect(result.timestamp).toBeDefined(); + }); + }); + + describe('when a valid bundle with inclusion proof is provided', () => { + const bundle = bundleFromJSON(bundlesv02.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial!.tlogEntries[0]; + + it('does NOT throw an error', () => { + const result = verifyTLogTimestamp(tlogEntry, [validTLog]); + + expect(result.type).toEqual('transparency-log'); + expect(result.logID).toEqual(keyID); + expect(result.timestamp).toBeDefined(); + }); + }); + + describe('when a valid bundle with NO inclusion proof/promise is provided', () => { + const bundle = bundleFromJSON(bundlesv02.signature.valid.withSigningCert); + + // Manipulate bundle to remove inclusion proof/promise + const tlogEntry = { + ...bundle.verificationMaterial?.tlogEntries[0], + inclusionProof: undefined, + inclusionPromise: undefined, + }; + + it('throws an error', () => { + expect(() => verifyTLogTimestamp(tlogEntry, [validTLog])).toThrowWithCode( + VerificationError, + 'TLOG_MISSING_INCLUSION_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/timestamp/merkle.test.ts b/packages/verify/src/__tests__/timestamp/merkle.test.ts new file mode 100644 index 00000000..76222086 --- /dev/null +++ b/packages/verify/src/__tests__/timestamp/merkle.test.ts @@ -0,0 +1,210 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto } from '@sigstore/core'; +import { fromPartial } from '@total-typescript/shoehorn'; +import { VerificationError } from '../../error'; +import { verifyMerkleInclusion } from '../../timestamp/merkle'; + +import type { TLogEntryWithInclusionProof } from '@sigstore/bundle'; + +describe('verifyMerkleInclusion', () => { + // Test data comes from https://rekor.sigstore.dev/api/v1/log/entries?logIndex=25591465 + const canonicalizedBody = Buffer.from( + 'eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU13VkVORFFXeGhaMEYzU1VKQlowbFZRMGxaYmpsclZWQkRTV2hqTVdKcFJFTTBhMlJQV1ROVGNXRm5kME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA1cVNUVk5WR040VFVSVk1WZG9ZMDVOYWsxM1RtcEpOVTFVWTNsTlJGVXhWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVVelZVOVVZWGhKVW5sUFdHbHJOM1JCVFRKT01rcFNkV04zVG1aRmNYUXdRbkE1Tm1FS2VHd3hORUpOV0ZRMWR5OW1lVzAwWmtNd1JFUnZUazVyY0ZaWVVtZHFhMjkxTTBjeFpsa3dZV3RzY2xJclJpOWFWWEZQUTBGWVZYZG5aMFo0VFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWWlRXbGxDa2xRVVhKWUwwdE5NM2xqTWtZeFNHRnhXSEZwTTJNMGQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxRE5FZERhWE5IUVZGUlFtYzNPSGRCVVdkRkNrbEJkMlZoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbmgyV2pKc2RVd3lPV2hrV0ZKdlRVbEhTMEpuYjNKQ1owVkZRV1JhTlVGblVVTUtRa2gzUldWblFqUkJTRmxCTTFRd2QyRnpZa2hGVkVwcVIxSTBZMjFYWXpOQmNVcExXSEpxWlZCTE15OW9OSEI1WjBNNGNEZHZORUZCUVVkS1EwTk1lUXAxVVVGQlFrRk5RVko2UWtaQmFVRnphbTh6YTBvd1lYWlFkelUwY2tGVVNHNVNRelF6TkZKUVpFMHZlbTlCVldsdFRuZERSQ3MyZVZsUlNXaEJURXQ0Q2pGdWVsUk9NSGxEYTBKdVRUbFlTMVkyUVdSRGVVdFdkMmh2TjFKeU1GbEdORW95WldGclMweE5RVzlIUTBOeFIxTk5ORGxDUVUxRVFUSnJRVTFIV1VNS1RWRkVORzgwWVRoa2VraDNZbFIwTmpJMU5FazFXWHBETVVoWVJVSnRlazQwVkVoYVpGQnBMMlpZZFc1T2NXTTVMMjF2V0d0cWQxcExTSE5DWWtSV09RcElOVmxEVFZGRVRVRTNaemhQWldwSU5rMTNVa2xpVXk5WFVUTjFOM1ZWV21WYVNsRnFTMFJHWWpkTmJHTmxRbFI0SzBRdlQzTnFkVkpOTldwWWRtNWFDa1l5UmxaeVYxVTlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMD0iLCJzaWciOiJUVVZWUTBsUlJFNXBWWGxPZWxwc05pOU1iblJ4ZUdoNVRsZExUbkZJYm1aRFFrWnNWRWxCYjNKdFNtSnFXRWRqZVZGSlowOVFSM0ZNYm5wWFlWWlJWbk5KUlZwQ1MyNXJURzlWVGtJMlJIWkRVVGRxT0RaTVJIZ3dObE13ZEZrOSJ9XX0sImhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI3OWJiMDRhN2VhMDA3M2FiN2VmY2NkYjlkOWM5NTQ0MWJiOWJkNjQ5ZGE5ZTc0YzNlNzkzMTVlZTk5NjFkYWE4In0sInBheWxvYWRIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiOGE3NWY2YzhkYzRkOWY0MDcyODViYjQ5ZDNlMDE5YTVlNjY2ZjQzNDk5YzI3MDdkOTI4OWFhMjdjM2MyYTY3ZSJ9fX19', + 'base64' + ); + const inclusionProof = { + logIndex: '21428034', + treeSize: '21476367', + hashes: [ + Buffer.from( + '19d3dbf73de6aefabc91f0b0e143b98aa85a09da0fe425ec5f1cd6d156f71618', + 'hex' + ), + Buffer.from( + 'd1142137fddf94069fea54345b912823d6b58b6c11988056581e14328cc1030f', + 'hex' + ), + Buffer.from( + '93dc3b1aa26f0abe487597beae4e195e906e749e8e4ddeee27e79dbde5b91402', + 'hex' + ), + Buffer.from( + '39f0b7d480d19cf281a3fd1bb9e22d2e00c4439872b4bbf93356761ff7924d01', + 'hex' + ), + Buffer.from( + 'c4dbc3de6933e3adec550dd14c7fee8959ae510524e8028c3e755c3c6b865be4', + 'hex' + ), + Buffer.from( + 'fb0c3969556c47a5da58cb9cd58de783f1d133f145b2a24ff6c7767ad0557b20', + 'hex' + ), + Buffer.from( + '8a8f4d600381615d5a9ebc191241cffa65299e80a5f48562f63d72630aedf0c6', + 'hex' + ), + Buffer.from( + '6e3cfb0b5ac7d32e7e58f51324748e12600d03fe293049fe36247fed3fb2fa65', + 'hex' + ), + Buffer.from( + 'ea20898f9069a9d85faf515f20b062b56b1ff4c1750ce4d741acead08b254b4a', + 'hex' + ), + Buffer.from( + '3c0c2711b5709e116362413734eebf10b2dcd81cadd2325502254585f5408a93', + 'hex' + ), + Buffer.from( + '293213bbbac895205eb11b6a4f905eeb2632182aa8022fc96c109d5fa9d9ea31', + 'hex' + ), + Buffer.from( + 'f6f08053bc2277b800e3bfbc74db76a24015f1f38a17e438fac9f3e3a49aa1d4', + 'hex' + ), + Buffer.from( + '0be5c7bbcf481d1efcfc63a27fce447cf6345f7bb3155cf5de39de592c16d52d', + 'hex' + ), + Buffer.from( + 'f597f4bae8df3d6fc6eebfe3eabd7d393e08781f6f16b42398eca8512398fff1', + 'hex' + ), + Buffer.from( + '4e35fcb3c0a59e7f329994002b38db35f5d511f499ba009e10b31f5d27563607', + 'hex' + ), + Buffer.from( + '47044b7ac3aab820e44f0010538d7de71e17a11f4140cbbe9eeb37f78b77cc7d', + 'hex' + ), + Buffer.from( + 'a096e8b56b363e063fb47944b05535e10247eae804325cc5c5df3d024b61e9bf', + 'hex' + ), + Buffer.from( + 'ff41aa21106dbe03996b4335dd158c7ffafd144e45022193de19b2b9136c3e42', + 'hex' + ), + Buffer.from( + 'e6ebdeef2e23335d8d7049ba5a0049a90593efdfe9c1b4548946b44a19d7214f', + 'hex' + ), + Buffer.from( + 'dd51e840e892d70093ad7e1db1e2dea3d50334c7345d360e444d22fc49ed9f5e', + 'hex' + ), + Buffer.from( + 'ad712c98424de0f1284d4f144b8a95b5d22c181d4c0a246518e7a9a220bdf643', + 'hex' + ), + ], + rootHash: Buffer.from( + 'd5c395a44b9537f8fa2524a6e93071969dd475ac40e87e2b1231b2aebd9a138b', + 'hex' + ), + }; + + describe('when the inclusion proof is valid', () => { + const entry: TLogEntryWithInclusionProof = fromPartial({ + canonicalizedBody, + inclusionProof, + }); + + it('does NOT throw an error', () => { + expect(verifyMerkleInclusion(entry)).toBeUndefined(); + }); + }); + + describe('when the entry does NOT match the inclusion proof', () => { + const invalidEntry: TLogEntryWithInclusionProof = fromPartial({ + canonicalizedBody: Buffer.from('invalid'), + inclusionProof, + }); + + it('throws an error', () => { + expect(() => verifyMerkleInclusion(invalidEntry)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when the log index is invalid', () => { + const invalidEntry: TLogEntryWithInclusionProof = fromPartial({ + canonicalizedBody, + inclusionProof: { ...inclusionProof, logIndex: '-1' }, + }); + + it('throws an error', () => { + expect(() => verifyMerkleInclusion(invalidEntry)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when the inclusion proof log index is greather than the tree size', () => { + const invalidEntry: TLogEntryWithInclusionProof = fromPartial({ + canonicalizedBody, + inclusionProof: { ...inclusionProof, treeSize: '99', logIndex: '100' }, + }); + + it('throws an error', () => { + expect(() => verifyMerkleInclusion(invalidEntry)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when the inclusion proof is missing hashes', () => { + const invalidEntry: TLogEntryWithInclusionProof = fromPartial({ + canonicalizedBody: Buffer.from('foo'), + inclusionProof: { + ...inclusionProof, + hashes: inclusionProof.hashes.slice(0, 1), + }, + }); + + it('throws an error', () => { + expect(() => verifyMerkleInclusion(invalidEntry)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROOF_ERROR' + ); + }); + }); + + describe('when we have the smallest possible tree', () => { + const body = Buffer.from('foo'); + const entry: TLogEntryWithInclusionProof = fromPartial({ + canonicalizedBody: body, + inclusionProof: { + hashes: [], + treeSize: '1', + logIndex: '0', + rootHash: crypto.hash(Buffer.from([0x00]), body), + }, + }); + + it('does NOT throw an error', () => { + expect(verifyMerkleInclusion(entry)).toBeUndefined(); + }); + }); +}); diff --git a/packages/verify/src/__tests__/timestamp/set.test.ts b/packages/verify/src/__tests__/timestamp/set.test.ts new file mode 100644 index 00000000..1562dee3 --- /dev/null +++ b/packages/verify/src/__tests__/timestamp/set.test.ts @@ -0,0 +1,119 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { + bundleFromJSON, + TLogEntryWithInclusionPromise, +} from '@sigstore/bundle'; +import { crypto } from '@sigstore/core'; +import { fromPartial } from '@total-typescript/shoehorn'; +import { VerificationError } from '../../error'; +import { verifyTLogSET } from '../../timestamp/set'; +import bundles from '../__fixtures__/bundles/v01'; + +import type { TLogAuthority } from '../../trust'; + +describe('verifyTLogSET', () => { + const keyBytes = Buffer.from( + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==', + 'base64' + ); + const keyID = crypto.hash(keyBytes); + + const validTLog: TLogAuthority = { + logID: keyID, + publicKey: crypto.createPublicKey(keyBytes), + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }; + + const invalidTLog: TLogAuthority = { + logID: Buffer.from('invalid'), + publicKey: crypto.createPublicKey(keyBytes), + validFor: { start: new Date(0), end: new Date('2100-01-01') }, + }; + + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const entry: TLogEntryWithInclusionPromise = fromPartial( + bundle.verificationMaterial?.tlogEntries[0] + ); + + describe('when there is a matching TLogInstance', () => { + const tlogs = [invalidTLog, validTLog]; + + describe('when the SET can be verified', () => { + it('does NOT throw an error', () => { + expect(verifyTLogSET(entry, tlogs)).toBeUndefined(); + }); + }); + + describe('when the SET can NOT be verified', () => { + const bundle = bundleFromJSON(bundles.signature.invalid.setMismatch); + const entry = bundle.verificationMaterial + ?.tlogEntries[0] as TLogEntryWithInclusionPromise; + + it('throws an error', () => { + expect(() => verifyTLogSET(entry, tlogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROMISE_ERROR' + ); + }); + }); + + describe('when the public key for the matching TLogInstance is not valid', () => { + describe('when the public key has a start after the integrated time', () => { + const tlogs = [ + { + ...validTLog, + validFor: { start: new Date(), end: new Date('2999-01-01') }, + }, + ]; + + it('throws an error', () => { + expect(() => verifyTLogSET(entry, tlogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROMISE_ERROR' + ); + }); + }); + + describe('when the public key has an end before the integrated time', () => { + const tlogs = [ + { + ...validTLog, + validFor: { start: new Date(0), end: new Date(0) }, + }, + ]; + + it('throws an error', () => { + expect(() => verifyTLogSET(entry, tlogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROMISE_ERROR' + ); + }); + }); + }); + }); + + describe('when there is NO matching TLogInstance', () => { + const tlogs = [invalidTLog]; + + it('throws an error', () => { + expect(() => verifyTLogSET(entry, tlogs)).toThrowWithCode( + VerificationError, + 'TLOG_INCLUSION_PROMISE_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/tlog/dsse.test.ts b/packages/verify/src/__tests__/tlog/dsse.test.ts new file mode 100644 index 00000000..b057e00e --- /dev/null +++ b/packages/verify/src/__tests__/tlog/dsse.test.ts @@ -0,0 +1,130 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { signatureContent } from '../../bundle'; +import { VerificationError } from '../../error'; +import { verifyDSSETLogBody } from '../../tlog/dsse'; +import bundles from '../__fixtures__/bundles/v01'; + +import type { ProposedDSSEEntry } from '@sigstore/rekor-types'; + +describe('verifyDSSETLogBody', () => { + describe('when everything is valid', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withDSSETLogEntry); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedDSSEEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('does NOT throw an error', () => { + expect(verifyDSSETLogBody(body, content)).toBeUndefined(); + }); + }); + + describe('when the apiVersion is unsupported', () => { + const bundle = bundleFromJSON(bundles.dsse.invalid.badSignatureTLogDSSE); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedDSSEEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + (body as any).apiVersion = '0.0.0'; + }); + + it('throws an error', () => { + expect(() => verifyDSSETLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the tlog entry body is missing the payload hash', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withDSSETLogEntry); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedDSSEEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + body.spec.payloadHash = undefined; + }); + + it('throws an error', () => { + expect(() => verifyDSSETLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the payload hash does NOT match the value in the dsse entry', () => { + const bundle = bundleFromJSON(bundles.dsse.invalid.badSignatureTLogDSSE); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedDSSEEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyDSSETLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the signature does NOT match the value in the dsse entry', () => { + const bundle = bundleFromJSON( + bundles.dsse.invalid.tlogDSSEIncorrectSigInBody + ); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedDSSEEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyDSSETLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the signature count does NOT match the dsse entry', () => { + const bundle = bundleFromJSON( + bundles.dsse.invalid.tlogDSSETooManySigsInBody + ); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedDSSEEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyDSSETLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/tlog/hashedrekord.test.ts b/packages/verify/src/__tests__/tlog/hashedrekord.test.ts new file mode 100644 index 00000000..d46ed350 --- /dev/null +++ b/packages/verify/src/__tests__/tlog/hashedrekord.test.ts @@ -0,0 +1,134 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { signatureContent } from '../../bundle'; +import { VerificationError } from '../../error'; +import { verifyHashedRekordTLogBody } from '../../tlog/hashedrekord'; +import bundles from '../__fixtures__/bundles/v01'; + +import type { ProposedHashedRekordEntry } from '@sigstore/rekor-types'; + +describe('verifyHashedRekordTLogBody', () => { + describe('when everything is valid', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedHashedRekordEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('does NOT throw an error', () => { + expect(verifyHashedRekordTLogBody(body, content)).toBeUndefined(); + }); + }); + + describe('when the apiVersion is unsupported', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedHashedRekordEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + (body as any).apiVersion = '0.0.0'; + }); + + it('throws an error', () => { + expect(() => verifyHashedRekordTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the tlog entry body is missing the signature', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedHashedRekordEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + body.spec.signature.content = undefined; + }); + + it('throws an error', () => { + expect(() => verifyHashedRekordTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the tlog entry body is missing the hash', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedHashedRekordEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + body.spec.data.hash = undefined; + }); + + it('throws an error', () => { + expect(() => verifyHashedRekordTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the signature does NOT match the value in the tlog entry', () => { + const bundle = bundleFromJSON( + bundles.signature.invalid.tlogIncorrectSigInBody + ); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedHashedRekordEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyHashedRekordTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the digest does NOT match the value in the tlog entry', () => { + const bundle = bundleFromJSON( + bundles.signature.invalid.tlogIncorrectDigestInBody + ); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedHashedRekordEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyHashedRekordTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/tlog/index.test.ts b/packages/verify/src/__tests__/tlog/index.test.ts new file mode 100644 index 00000000..91845888 --- /dev/null +++ b/packages/verify/src/__tests__/tlog/index.test.ts @@ -0,0 +1,65 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { signatureContent } from '../../bundle'; +import { VerificationError } from '../../error'; +import { verifyTLogBody } from '../../tlog'; +import bundles from '../__fixtures__/bundles/v01'; + +describe('verifyTLogBody', () => { + describe('when the tlog entry matches (hashedrekord)', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const content = signatureContent(bundle); + + it('does NOT throw an error', () => { + expect(verifyTLogBody(tlogEntry, content)).toBeUndefined(); + }); + }); + + describe('when the tlog entry matches (intoto)', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const content = signatureContent(bundle); + + it('does NOT throw an error', () => { + expect(verifyTLogBody(tlogEntry, content)).toBeUndefined(); + }); + }); + + describe('when the tlog entry matches (dsse)', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withDSSETLogEntry); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const content = signatureContent(bundle); + + it('does NOT throw an error', () => { + expect(verifyTLogBody(tlogEntry, content)).toBeUndefined(); + }); + }); + + describe('when there is a version mismatch between the tlog entry and the body', () => { + const bundle = bundleFromJSON(bundles.dsse.invalid.tlogVersionMismatch); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyTLogBody(tlogEntry, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/tlog/intoto.test.ts b/packages/verify/src/__tests__/tlog/intoto.test.ts new file mode 100644 index 00000000..cdc8e339 --- /dev/null +++ b/packages/verify/src/__tests__/tlog/intoto.test.ts @@ -0,0 +1,130 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { signatureContent } from '../../bundle'; +import { VerificationError } from '../../error'; +import { verifyIntotoTLogBody } from '../../tlog/intoto'; +import bundles from '../__fixtures__/bundles/v01'; + +import type { ProposedIntotoEntry } from '@sigstore/rekor-types'; + +describe('verifyIntotoTLogBody', () => { + describe('when everything is valid', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedIntotoEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('does NOT throw an error', () => { + expect(verifyIntotoTLogBody(body, content)).toBeUndefined(); + }); + }); + + describe('when the payload hash does NOT match the value in the intoto entry', () => { + const bundle = bundleFromJSON(bundles.dsse.invalid.badSignatureTLogIntoto); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedIntotoEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyIntotoTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the signature does NOT match the value in the intoto entry', () => { + const bundle = bundleFromJSON( + bundles.dsse.invalid.tlogIntotoIncorrectSigInBody + ); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedIntotoEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyIntotoTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the tlog entry version is unsupported', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedIntotoEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + (body as any).apiVersion = '0.0.0'; + }); + + it('throws an error', () => { + expect(() => verifyIntotoTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the signature count does NOT match the intoto entry', () => { + const bundle = bundleFromJSON( + bundles.dsse.invalid.tlogIntotoTooManySigsInBody + ); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedIntotoEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + it('throws an error', () => { + expect(() => verifyIntotoTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); + + describe('when the tlog entry body is missing the payload hash', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withSigningCert); + const tlogEntry = bundle.verificationMaterial.tlogEntries[0]; + const body: ProposedIntotoEntry = JSON.parse( + tlogEntry.canonicalizedBody.toString('utf8') + ); + const content = signatureContent(bundle); + + beforeEach(() => { + body.spec.content.payloadHash = undefined; + }); + + it('throws an error', () => { + expect(() => verifyIntotoTLogBody(body, content)).toThrowWithCode( + VerificationError, + 'TLOG_BODY_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/trust/filter.test.ts b/packages/verify/src/__tests__/trust/filter.test.ts new file mode 100644 index 00000000..52d1c668 --- /dev/null +++ b/packages/verify/src/__tests__/trust/filter.test.ts @@ -0,0 +1,72 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { fromPartial } from '@total-typescript/shoehorn'; +import { filterTLogAuthorities } from '../../trust/filter'; + +import type { TLogAuthority } from '../../trust'; + +describe('filterTLogAuthorities', () => { + const tlogInstances: TLogAuthority[] = [ + { + logID: Buffer.from('log1'), + publicKey: fromPartial({}), + validFor: { + start: new Date('2020-01-01'), + end: new Date('2020-12-31'), + }, + }, + { + logID: Buffer.from('log2'), + publicKey: fromPartial({}), + validFor: { + start: new Date('1900-01-01'), + end: new Date('1900-12-31'), + }, + }, + { + logID: Buffer.from('log3'), + publicKey: fromPartial({}), + validFor: { + start: new Date('2020-01-01'), + end: new Date('2020-12-31'), + }, + }, + ]; + + describe('when filtering by date', () => { + it('returns instances valid during the given date', () => { + const tlogs = filterTLogAuthorities(tlogInstances, { + targetDate: new Date('2020-02-01'), + }); + + expect(tlogs).toHaveLength(2); + expect(tlogs[0].logID).toEqual(Buffer.from('log1')); + expect(tlogs[1].logID).toEqual(Buffer.from('log3')); + }); + }); + + describe('when filtering by date and log ID', () => { + it('returns instances valid during the given date for the given log ID', () => { + const tlogs = filterTLogAuthorities(tlogInstances, { + targetDate: new Date('1900-02-01'), + logID: Buffer.from('log2'), + }); + + expect(tlogs).toHaveLength(1); + expect(tlogs[0].logID).toEqual(Buffer.from('log2')); + }); + }); +}); diff --git a/packages/verify/src/__tests__/trust/index.test.ts b/packages/verify/src/__tests__/trust/index.test.ts new file mode 100644 index 00000000..1a0966de --- /dev/null +++ b/packages/verify/src/__tests__/trust/index.test.ts @@ -0,0 +1,78 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { VerificationError } from '../../error'; +import { toTrustMaterial } from '../../trust'; +import { trustedRoot } from '../__fixtures__/trust'; + +describe('toTrustMaterial', () => { + it('returns a TrustMaterial', () => { + const result = toTrustMaterial(trustedRoot); + expect(result).toBeDefined(); + expect(result.certificateAuthorities).toHaveLength(2); + expect(result.timestampAuthorities).toHaveLength(0); + expect(result.tlogs).toHaveLength(1); + expect(result.ctlogs).toHaveLength(2); + + expect(() => result.publicKey('FOO')).toThrowWithCode( + VerificationError, + 'PUBLIC_KEY_ERROR' + ); + }); + + describe('when provided with keys', () => { + const keys = { + FOO: { + rawBytes: Buffer.from( + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==', + 'base64' + ), + keyDetails: 0, + validFor: { + start: undefined, + }, + }, + BAR: { + rawBytes: Buffer.from( + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==', + 'base64' + ), + keyDetails: 0, + validFor: { + start: new Date(0), + end: new Date(0), + }, + }, + }; + + it('implements a key look-up function', () => { + const result = toTrustMaterial(trustedRoot, keys); + expect(result.publicKey).toBeDefined(); + + const key1 = result.publicKey('FOO'); + expect(key1).toBeDefined(); + expect(key1.validFor(new Date())).toBe(true); + + const key2 = result.publicKey('BAR'); + expect(key2).toBeDefined(); + expect(key2.validFor(new Date())).toBe(false); + + expect(() => result.publicKey('BEEF')).toThrowWithCode( + VerificationError, + 'PUBLIC_KEY_ERROR' + ); + }); + }); +}); diff --git a/packages/verify/src/__tests__/verifier.test.ts b/packages/verify/src/__tests__/verifier.test.ts new file mode 100644 index 00000000..9f4c0f84 --- /dev/null +++ b/packages/verify/src/__tests__/verifier.test.ts @@ -0,0 +1,143 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { bundleFromJSON } from '@sigstore/bundle'; +import { crypto } from '@sigstore/core'; +import { PublicKeyDetails } from '@sigstore/protobuf-specs'; +import { fromPartial } from '@total-typescript/shoehorn'; +import { toSignedEntity } from '../bundle'; +import { VerificationError } from '../error'; +import { TrustMaterial, toTrustMaterial } from '../trust'; +import { Verifier } from '../verifier'; +import bundles from './__fixtures__/bundles/v01'; +import { trustedRoot } from './__fixtures__/trust'; + +describe('Verifier', () => { + describe('constructor', () => { + const trustMaterial: TrustMaterial = fromPartial({}); + + it('should create a new instance', () => { + const verifier = new Verifier(trustMaterial); + expect(verifier).toBeDefined(); + }); + }); + + describe('#verify', () => { + const publicKey = crypto.createPublicKey(bundles.signature.publicKey); + + const keys = { + '9a76331edc1cfd3933040996615b1c06adbe6f9b4f11df4106dcceb66e3bdb1b': { + rawBytes: publicKey.export({ type: 'spki', format: 'der' }), + keyDetails: PublicKeyDetails.PKIX_ECDSA_P256_SHA_256, + validFor: { start: new Date(0) }, + }, + }; + const trustMaterial = toTrustMaterial(trustedRoot, keys); + const subject = new Verifier(trustMaterial); + + describe('when the certificate-signed message signature bundle is valid', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const signedEntity = toSignedEntity(bundle, bundles.signature.artifact); + + it('returns without error', () => { + subject.verify(signedEntity); + }); + }); + + describe('when the key-signed message signature bundle is valid', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withPublicKey); + const signedEntity = toSignedEntity(bundle, bundles.signature.artifact); + + it('returns without error', () => { + subject.verify(signedEntity); + }); + }); + + describe('when the certificate-signed DSSE bundle is valid', () => { + const bundle = bundleFromJSON(bundles.dsse.valid.withSigningCert); + const signedEntity = toSignedEntity(bundle); + + it('returns without error', () => { + subject.verify(signedEntity); + }); + }); + + describe('when the bundle contains duplicate TLog entries', () => { + const bundle = bundleFromJSON(bundles.signature.invalid.duplicateTLog); + const signedEntity = toSignedEntity(bundle, bundles.signature.artifact); + + it('throws an error', () => { + expect(() => subject.verify(signedEntity)).toThrowWithCode( + VerificationError, + 'TIMESTAMP_ERROR' + ); + }); + }); + + describe('when the tlog timestamp threshold is not met', () => { + const subject = new Verifier(trustMaterial, { tlogThreshold: 2 }); + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const signedEntity = toSignedEntity(bundle, bundles.signature.artifact); + + it('throws an error', () => { + expect(() => subject.verify(signedEntity)).toThrowWithCode( + VerificationError, + 'TIMESTAMP_ERROR' + ); + }); + }); + + describe('when the tsa timestamp threshold is not met', () => { + const subject = new Verifier(trustMaterial, { tsaThreshold: 2 }); + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const signedEntity = toSignedEntity(bundle, bundles.signature.artifact); + + it('throws an error', () => { + expect(() => subject.verify(signedEntity)).toThrowWithCode( + VerificationError, + 'TIMESTAMP_ERROR' + ); + }); + }); + + describe('when the ctlog threshold is not met', () => { + const subject = new Verifier(trustMaterial, { ctlogThreshold: 2 }); + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const signedEntity = toSignedEntity(bundle, bundles.signature.artifact); + + it('throws an error', () => { + expect(() => subject.verify(signedEntity)).toThrowWithCode( + VerificationError, + 'CERTIFICATE_ERROR' + ); + }); + }); + + describe('when the bundle signature cannot be verified', () => { + const bundle = bundleFromJSON(bundles.signature.valid.withSigningCert); + const signedEntity = toSignedEntity( + bundle, + Buffer.from('not the artifact') + ); + + it('throws an error', () => { + expect(() => subject.verify(signedEntity)).toThrowWithCode( + VerificationError, + 'SIGNATURE_ERROR' + ); + }); + }); + }); +}); diff --git a/packages/verify/src/bundle/dsse.ts b/packages/verify/src/bundle/dsse.ts new file mode 100644 index 00000000..06d980ba --- /dev/null +++ b/packages/verify/src/bundle/dsse.ts @@ -0,0 +1,50 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto, dsse } from '@sigstore/core'; + +import type { Envelope } from '@sigstore/bundle'; +import type { SignatureContent } from '../shared.types'; + +export class DSSESignatureContent implements SignatureContent { + private readonly env: Envelope; + + constructor(env: Envelope) { + this.env = env; + } + + public compareDigest(digest: Buffer): boolean { + return crypto.bufferEqual(digest, crypto.hash(this.env.payload)); + } + + public compareSignature(signature: Buffer): boolean { + return crypto.bufferEqual(signature, this.signature); + } + + public verifySignature(key: crypto.KeyObject): boolean { + return crypto.verify(this.preAuthEncoding, key, this.signature); + } + + private get signature(): Buffer { + return this.env.signatures.length > 0 + ? this.env.signatures[0].sig + : Buffer.from(''); + } + + // DSSE Pre-Authentication Encoding + private get preAuthEncoding(): Buffer { + return dsse.preAuthEncoding(this.env.payloadType, this.env.payload); + } +} diff --git a/packages/verify/src/bundle/index.ts b/packages/verify/src/bundle/index.ts new file mode 100644 index 00000000..a976013b --- /dev/null +++ b/packages/verify/src/bundle/index.ts @@ -0,0 +1,75 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { Bundle, TransparencyLogEntry } from '@sigstore/bundle'; +import { X509Certificate } from '@sigstore/core'; +import { DSSESignatureContent } from './dsse'; +import { MessageSignatureContent } from './message'; + +import type { + SignatureContent, + SignedEntity, + VerificationKey, +} from '../shared.types'; + +export function toSignedEntity( + bundle: Bundle, + artifact?: Buffer +): SignedEntity { + return { + signature: signatureContent(bundle, artifact), + key: key(bundle), + tlogEntries: bundle.verificationMaterial.tlogEntries, + timestamps: bundle.verificationMaterial.tlogEntries.map( + (entry: TransparencyLogEntry) => ({ + $case: 'transparency-log', + tlogEntry: entry, + }) + ), + }; +} + +export function signatureContent( + bundle: Bundle, + artifact?: Buffer +): SignatureContent { + switch (bundle.content.$case) { + case 'dsseEnvelope': + return new DSSESignatureContent(bundle.content.dsseEnvelope); + case 'messageSignature': + return new MessageSignatureContent( + bundle.content.messageSignature, + artifact! + ); + } +} + +function key(bundle: Bundle): VerificationKey { + switch (bundle.verificationMaterial.content.$case) { + case 'publicKey': + return { + $case: 'public-key', + hint: bundle.verificationMaterial.content.publicKey.hint, + }; + case 'x509CertificateChain': + return { + $case: 'certificate', + certificate: X509Certificate.parse( + bundle.verificationMaterial.content.x509CertificateChain + .certificates[0].rawBytes + ), + }; + } +} diff --git a/packages/verify/src/bundle/message.ts b/packages/verify/src/bundle/message.ts new file mode 100644 index 00000000..b2b49bf8 --- /dev/null +++ b/packages/verify/src/bundle/message.ts @@ -0,0 +1,43 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto } from '@sigstore/core'; + +import type { MessageSignature } from '@sigstore/bundle'; +import type { SignatureContent } from '../shared.types'; + +export class MessageSignatureContent implements SignatureContent { + private readonly signature: Buffer; + private readonly messageDigest: Buffer; + private readonly artifact: Buffer; + + constructor(messageSignature: MessageSignature, artifact: Buffer) { + this.signature = messageSignature.signature; + this.messageDigest = messageSignature.messageDigest.digest; + this.artifact = artifact; + } + + public compareSignature(signature: Buffer): boolean { + return crypto.bufferEqual(signature, this.signature); + } + + public compareDigest(digest: Buffer): boolean { + return crypto.bufferEqual(digest, this.messageDigest); + } + + public verifySignature(key: crypto.KeyObject): boolean { + return crypto.verify(this.artifact, key, this.signature); + } +} diff --git a/packages/verify/src/error.ts b/packages/verify/src/error.ts new file mode 100644 index 00000000..dececef2 --- /dev/null +++ b/packages/verify/src/error.ts @@ -0,0 +1,47 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +class BaseError extends Error { + code: T; + cause: any; /* eslint-disable-line @typescript-eslint/no-explicit-any */ + + constructor({ + code, + message, + cause, + }: { + code: T; + message: string; + cause?: any /* eslint-disable-line @typescript-eslint/no-explicit-any */; + }) { + super(message); + this.code = code; + this.cause = cause; + this.name = this.constructor.name; + } +} + +type VerificationErrorCode = + | 'NOT_IMPLEMENTED_ERROR' + | 'TLOG_INCLUSION_PROOF_ERROR' + | 'TLOG_INCLUSION_PROMISE_ERROR' + | 'TLOG_MISSING_INCLUSION_ERROR' + | 'TLOG_BODY_ERROR' + | 'CERTIFICATE_ERROR' + | 'PUBLIC_KEY_ERROR' + | 'SIGNATURE_ERROR' + | 'TIMESTAMP_ERROR'; + +export class VerificationError extends BaseError {} diff --git a/packages/verify/src/index.ts b/packages/verify/src/index.ts new file mode 100644 index 00000000..35f18a60 --- /dev/null +++ b/packages/verify/src/index.ts @@ -0,0 +1,20 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +export { toSignedEntity } from './bundle'; +export { TrustMaterial, toTrustMaterial } from './trust'; +export { Verifier, VerifierOptions } from './verifier'; + +export type { SignedEntity, Signer } from './shared.types'; diff --git a/packages/verify/src/key/certificate.ts b/packages/verify/src/key/certificate.ts new file mode 100644 index 00000000..3dc80058 --- /dev/null +++ b/packages/verify/src/key/certificate.ts @@ -0,0 +1,269 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { X509Certificate } from '@sigstore/core'; +import { VerificationError } from '../error'; +import { filterCertAuthorities } from '../trust'; + +import { CertAuthority } from '../trust'; + +export function verifyCertificateChain( + leaf: X509Certificate, + certificateAuthorities: CertAuthority[] +): X509Certificate[] { + // Filter list of trusted CAs to those which are valid for the given + // leaf certificate. + const cas = filterCertAuthorities(certificateAuthorities, { + start: leaf.notBefore, + end: leaf.notAfter, + }); + + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + let error: any; + for (const ca of cas) { + try { + const verifier = new CertificateChainVerifier({ + trustedCerts: ca.certChain, + untrustedCert: leaf, + }); + return verifier.verify(); + } catch (err) { + error = err; + } + } + + // If we failed to verify the certificate chain for all of the trusted + // CAs, throw the last error we encountered. + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'Failed to verify certificate chain', + cause: error, + }); +} + +interface CertificateChainVerifierOptions { + trustedCerts: X509Certificate[]; + untrustedCert: X509Certificate; +} + +class CertificateChainVerifier { + private untrustedCert: X509Certificate; + private trustedCerts: X509Certificate[]; + private localCerts: X509Certificate[]; + + constructor(opts: CertificateChainVerifierOptions) { + this.untrustedCert = opts.untrustedCert; + this.trustedCerts = opts.trustedCerts; + this.localCerts = dedupeCertificates([ + ...opts.trustedCerts, + opts.untrustedCert, + ]); + } + + public verify(): X509Certificate[] { + // Construct certificate path from leaf to root + const certificatePath = this.sort(); + + // Perform validation checks on each certificate in the path + this.checkPath(certificatePath); + + // Return verified certificate path + return certificatePath; + } + + private sort(): X509Certificate[] { + const leafCert = this.untrustedCert; + + // Construct all possible paths from the leaf + let paths = this.buildPaths(leafCert); + + // Filter for paths which contain a trusted certificate + paths = paths.filter((path) => + path.some((cert) => this.trustedCerts.includes(cert)) + ); + + if (paths.length === 0) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'no trusted certificate path found', + }); + } + + // Find the shortest of possible paths + /* istanbul ignore next */ + const path = paths.reduce((prev, curr) => + prev.length < curr.length ? prev : curr + ); + + // Construct chain from shortest path + // Removes the last certificate in the path, which will be a second copy + // of the root certificate given that the root is self-signed. + return [leafCert, ...path].slice(0, -1); + } + + // Recursively build all possible paths from the leaf to the root + private buildPaths(certificate: X509Certificate): X509Certificate[][] { + const paths = []; + const issuers = this.findIssuer(certificate); + + if (issuers.length === 0) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'no valid certificate path found', + }); + } + + for (let i = 0; i < issuers.length; i++) { + const issuer = issuers[i]; + + // Base case - issuer is self + if (issuer.equals(certificate)) { + paths.push([certificate]); + continue; + } + + // Recursively build path for the issuer + const subPaths = this.buildPaths(issuer); + + // Construct paths by appending the issuer to each subpath + for (let j = 0; j < subPaths.length; j++) { + paths.push([issuer, ...subPaths[j]]); + } + } + + return paths; + } + + // Return all possible issuers for the given certificate + private findIssuer(certificate: X509Certificate): X509Certificate[] { + let issuers: X509Certificate[] = []; + let keyIdentifier: Buffer | undefined; + + // Exit early if the certificate is self-signed + if (certificate.subject.equals(certificate.issuer)) { + if (certificate.verify()) { + return [certificate]; + } + } + + // If the certificate has an authority key identifier, use that + // to find the issuer + if (certificate.extAuthorityKeyID) { + keyIdentifier = certificate.extAuthorityKeyID.keyIdentifier; + + // TODO: Add support for authorityCertIssuer/authorityCertSerialNumber + // though Fulcio doesn't appear to use these + } + + // Find possible issuers by comparing the authorityKeyID/subjectKeyID + // or issuer/subject. Potential issuers are added to the result array. + this.localCerts.forEach((possibleIssuer) => { + if (keyIdentifier) { + if (possibleIssuer.extSubjectKeyID) { + if ( + possibleIssuer.extSubjectKeyID.keyIdentifier.equals(keyIdentifier) + ) { + issuers.push(possibleIssuer); + } + return; + } + } + + // Fallback to comparing certificate issuer and subject if + // subjectKey/authorityKey extensions are not present + if (possibleIssuer.subject.equals(certificate.issuer)) { + issuers.push(possibleIssuer); + } + }); + + // Remove any issuers which fail to verify the certificate + issuers = issuers.filter((issuer) => { + try { + return certificate.verify(issuer); + } catch (ex) { + /* istanbul ignore next - should never error */ + return false; + } + }); + + return issuers; + } + + private checkPath(path: X509Certificate[]): void { + /* istanbul ignore if */ + if (path.length < 1) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'certificate chain must contain at least one certificate', + }); + } + + // Ensure that all certificates beyond the leaf are CAs + const validCAs = path.slice(1).every((cert) => cert.isCA); + if (!validCAs) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'intermediate certificate is not a CA', + }); + } + + // Certificate's issuer must match the subject of the next certificate + // in the chain + for (let i = path.length - 2; i >= 0; i--) { + /* istanbul ignore if */ + if (!path[i].issuer.equals(path[i + 1].subject)) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'incorrect certificate name chaining', + }); + } + } + + // Check pathlength constraints + for (let i = 0; i < path.length; i++) { + const cert = path[i]; + + // If the certificate is a CA, check the path length + if (cert.extBasicConstraints?.isCA) { + const pathLength = cert.extBasicConstraints.pathLenConstraint; + + // The path length, if set, indicates how many intermediate + // certificates (NOT including the leaf) are allowed to follow. The + // pathLength constraint of any intermediate CA certificate MUST be + // greater than or equal to it's own depth in the chain (with an + // adjustment for the leaf certificate) + if (pathLength !== undefined && pathLength < i - 1) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'path length constraint exceeded', + }); + } + } + } + } +} + +// Remove duplicate certificates from the array +function dedupeCertificates(certs: X509Certificate[]): X509Certificate[] { + for (let i = 0; i < certs.length; i++) { + for (let j = i + 1; j < certs.length; j++) { + if (certs[i].equals(certs[j])) { + certs.splice(j, 1); + j--; + } + } + } + return certs; +} diff --git a/packages/verify/src/key/index.ts b/packages/verify/src/key/index.ts new file mode 100644 index 00000000..824c56c2 --- /dev/null +++ b/packages/verify/src/key/index.ts @@ -0,0 +1,91 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { X509Certificate, crypto } from '@sigstore/core'; +import { VerificationError } from '../error'; +import { verifyCertificateChain } from './certificate'; +import { VerifiedSCTProvider, verifySCTs } from './sct'; + +import type { CertificateIdentity, Signer } from '../shared.types'; +import type { TrustMaterial } from '../trust'; + +const OID_FULCIO_ISSUER = '1.3.6.1.4.1.57264.1.1'; + +export type CertificateVerificationResult = { + signer: Signer; + scts: VerifiedSCTProvider[]; +}; + +export function verifyPublicKey( + hint: string, + timestamps: Date[], + trustMaterial: TrustMaterial +): Signer { + const key = trustMaterial.publicKey(hint); + + timestamps.forEach((timestamp) => { + if (!key.validFor(timestamp)) { + throw new VerificationError({ + code: 'PUBLIC_KEY_ERROR', + message: `Public key is not valid for timestamp: ${timestamp.toISOString()}`, + }); + } + }); + + return { key: key.publicKey }; +} + +export function verifyCertificate( + leaf: X509Certificate, + timestamps: Date[], + trustMaterial: TrustMaterial +): CertificateVerificationResult { + // Check that leaf certificate chains to a trusted CA + const path = verifyCertificateChain( + leaf, + trustMaterial.certificateAuthorities + ); + + // Check that ALL certificates are valid for ALL of the timestamps + const validForDate = timestamps.every((timestamp) => + path.every((cert) => cert.validForDate(timestamp)) + ); + + if (!validForDate) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'certificate is not valid or expired at the specified date', + }); + } + + return { + scts: verifySCTs(path[0], path[1], trustMaterial.ctlogs), + signer: getSigner(path[0]), + }; +} + +function getSigner(cert: X509Certificate): Signer { + const identity: CertificateIdentity = { + extensions: { + issuer: cert.extension(OID_FULCIO_ISSUER)?.value.toString('ascii'), + }, + subjectAlternativeName: cert.subjectAltName, + }; + + return { + key: crypto.createPublicKey(cert.publicKey), + identity, + }; +} diff --git a/packages/verify/src/key/sct.ts b/packages/verify/src/key/sct.ts new file mode 100644 index 00000000..eb4c60ca --- /dev/null +++ b/packages/verify/src/key/sct.ts @@ -0,0 +1,104 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { + ByteStream, + EXTENSION_OID_SCT, + X509Certificate, + X509SCTExtension, + crypto, +} from '@sigstore/core'; +import { VerificationError } from '../error'; +import { filterTLogAuthorities } from '../trust'; + +import type { TLogAuthority } from '../trust'; +export type VerifiedSCTProvider = Buffer; + +export function verifySCTs( + cert: X509Certificate, + issuer: X509Certificate, + ctlogs: TLogAuthority[] +): VerifiedSCTProvider[] { + let extSCT: X509SCTExtension | undefined; + + // Verifying the SCT requires that we remove the SCT extension and + // re-encode the TBS structure to DER -- this value is part of the data + // over which the signature is calculated. Since this is a destructive action + // we create a copy of the certificate so we can remove the SCT extension + // without affecting the original certificate. + const clone = cert.clone(); + + // Intentionally not using the findExtension method here because we want to + // remove the the SCT extension from the certificate before calculating the + // PreCert structure + for (let i = 0; i < clone.extensions.length; i++) { + const ext = clone.extensions[i]; + + if (ext.subs[0].toOID() === EXTENSION_OID_SCT) { + extSCT = new X509SCTExtension(ext); + + // Remove the extension from the certificate + clone.extensions.splice(i, 1); + break; + } + } + + // No SCT extension found to verify + if (!extSCT) { + return []; + } + + // Found an SCT extension but it has no SCTs + /* istanbul ignore if -- too difficult to fabricate test case for this */ + if (extSCT.signedCertificateTimestamps.length === 0) { + return []; + } + + // Construct the PreCert structure + // https://www.rfc-editor.org/rfc/rfc6962#section-3.2 + const preCert = new ByteStream(); + + // Calculate hash of the issuer's public key + const issuerId = crypto.hash(issuer.publicKey); + preCert.appendView(issuerId); + + // Re-encodes the certificate to DER after removing the SCT extension + const tbs = clone.tbsCertificate.toDER(); + preCert.appendUint24(tbs.length); + preCert.appendView(tbs); + + // Calculate and return the verification results for each SCT + return extSCT.signedCertificateTimestamps.map((sct) => { + // Find the ctlog instance that corresponds to the SCT's logID + const validCTLogs = filterTLogAuthorities(ctlogs, { + logID: sct.logID, + targetDate: sct.datetime, + }); + + // See if the SCT is valid for any of the CT logs + const verified = validCTLogs.some((log) => + sct.verify(preCert.buffer, log.publicKey) + ); + + if (!verified) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'SCT verification failed', + }); + } + + return sct.logID; + }); +} diff --git a/packages/verify/src/shared.types.ts b/packages/verify/src/shared.types.ts new file mode 100644 index 00000000..22de4970 --- /dev/null +++ b/packages/verify/src/shared.types.ts @@ -0,0 +1,81 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import type { TransparencyLogEntry } from '@sigstore/bundle'; +import type { X509Certificate, crypto } from '@sigstore/core'; + +export type CertificateExtensions = { + issuer: string; +}; + +export type CertificateIdentity = { + subjectAlternativeName?: string; + extensions: Partial; +}; + +export type Signer = { + key: crypto.KeyObject; + identity?: CertificateIdentity; +}; + +// TODO: Implement this! +export type RFC3161Timestamp = object; + +export type Timestamp = + | { + $case: 'timestamp-authority'; + timestamp: RFC3161Timestamp; + } + | { + $case: 'transparency-log'; + tlogEntry: TransparencyLogEntry; + }; + +export type VerificationKey = + | { + $case: 'public-key'; + hint: string; + } + | { + $case: 'certificate'; + certificate: X509Certificate; + }; + +export type SignatureContent = { + compareSignature(signature: Buffer): boolean; + compareDigest(digest: Buffer): boolean; + verifySignature(key: crypto.KeyObject): boolean; +}; + +export type TimestampProvider = { + timestamps: Timestamp[]; +}; + +export type SignatureProvider = { + signature: SignatureContent; +}; + +export type KeyProvider = { + key: VerificationKey; +}; + +export type TLogEntryProvider = { + tlogEntries: TransparencyLogEntry[]; +}; + +export type SignedEntity = SignatureProvider & + KeyProvider & + TimestampProvider & + TLogEntryProvider; diff --git a/packages/verify/src/timestamp/checkpoint.ts b/packages/verify/src/timestamp/checkpoint.ts new file mode 100644 index 00000000..78d71fd4 --- /dev/null +++ b/packages/verify/src/timestamp/checkpoint.ts @@ -0,0 +1,207 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto } from '@sigstore/core'; +import { VerificationError } from '../error'; +import { filterTLogAuthorities, TLogAuthority } from '../trust'; + +import type { TLogEntryWithInclusionProof } from '@sigstore/bundle'; + +// Separator between the note and the signatures in a checkpoint +const CHECKPOINT_SEPARATOR = '\n\n'; + +// Checkpoint signatures are of the following form: +// "– \n" +// where: +// - the prefix is an emdash (U+2014). +// - gives a human-readable representation of the signing ID. +// - is the first 4 bytes of the SHA256 hash of the +// associated public key followed by the signature bytes. +const SIGNATURE_REGEX = /\u2014 (\S+) (\S+)\n/g; + +interface TLogSignature { + name: string; + keyHint: Buffer; + signature: Buffer; +} + +// Verifies the checkpoint value in the given tlog entry. There are two steps +// to the verification: +// 1. Verify that all signatures in the checkpoint can be verified against a +// trusted public key +// 2. Verify that the root hash in the checkpoint matches the root hash in the +// inclusion proof +// See: https://github.com/transparency-dev/formats/blob/main/log/README.md +export function verifyCheckpoint( + entry: TLogEntryWithInclusionProof, + tlogs: TLogAuthority[] +): void { + // Filter tlog instances to just those which were valid at the time of the + // entry + const validTLogs = filterTLogAuthorities(tlogs, { + targetDate: new Date(Number(entry.integratedTime) * 1000), + }); + + const inclusionProof = entry.inclusionProof; + const signedNote = SignedNote.fromString(inclusionProof.checkpoint.envelope); + const checkpoint = LogCheckpoint.fromString(signedNote.note); + + // Verify that the signatures in the checkpoint are all valid + if (!verifySignedNote(signedNote, validTLogs)) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'invalid checkpoint signature', + }); + } + + // Verify that the root hash from the checkpoint matches the root hash in the + // inclusion proof + if (!crypto.bufferEqual(checkpoint.logHash, inclusionProof.rootHash)) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'root hash mismatch', + }); + } +} + +// Verifies the signatures in the SignedNote. For each signature, the +// corresponding transparency log is looked up by the key hint and the +// signature is verified against the public key in the transparency log. +// Throws an error if any of the signatures are invalid. +function verifySignedNote( + signedNote: SignedNote, + tlogs: TLogAuthority[] +): boolean { + const data = Buffer.from(signedNote.note, 'utf-8'); + + return signedNote.signatures.every((signature) => { + // Find the transparency log instance with the matching key hint + const tlog = tlogs.find((tlog) => + crypto.bufferEqual(tlog.logID.subarray(0, 4), signature.keyHint) + ); + + if (!tlog) { + return false; + } + + return crypto.verify(data, tlog.publicKey, signature.signature); + }); +} + +// SignedNote represents a signed note from a transparency log checkpoint. Consists +// of a body (or note) and one more signatures calculated over the body. See +// https://github.com/transparency-dev/formats/blob/main/log/README.md#signed-envelope +class SignedNote { + readonly note: string; + readonly signatures: TLogSignature[]; + + constructor(note: string, signatures: TLogSignature[]) { + this.note = note; + this.signatures = signatures; + } + + // Deserialize a SignedNote from a string + static fromString(envelope: string): SignedNote { + if (!envelope.includes(CHECKPOINT_SEPARATOR)) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'missing checkpoint separator', + }); + } + + // Split the note into the header and the data portions at the separator + const split = envelope.indexOf(CHECKPOINT_SEPARATOR); + const header = envelope.slice(0, split + 1); + const data = envelope.slice(split + CHECKPOINT_SEPARATOR.length); + + // Find all the signature lines in the data portion + const matches = data.matchAll(SIGNATURE_REGEX); + + // Parse each of the matched signature lines into the name and signature. + // The first four bytes of the signature are the key hint (should match the + // first four bytes of the log ID), and the rest is the signature itself. + const signatures = Array.from(matches, (match) => { + const [, name, signature] = match; + const sigBytes = Buffer.from(signature, 'base64'); + + if (sigBytes.length < 5) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'malformed checkpoint signature', + }); + } + + return { + name, + keyHint: sigBytes.subarray(0, 4), + signature: sigBytes.subarray(4), + }; + }); + + if (signatures.length === 0) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'no signatures found in checkpoint', + }); + } + + return new SignedNote(header, signatures); + } +} + +// LogCheckpoint represents a transparency log checkpoint. Consists of the +// following: +// - origin: the name of the transparency log +// - logSize: the size of the log at the time of the checkpoint +// - logHash: the root hash of the log at the time of the checkpoint +// - rest: the rest of the checkpoint body, which is a list of log entries +// See: +// https://github.com/transparency-dev/formats/blob/main/log/README.md#checkpoint-body +class LogCheckpoint { + readonly origin: string; + readonly logSize: bigint; + readonly logHash: Buffer; + readonly rest: string[]; + + constructor( + origin: string, + logSize: bigint, + logHash: Buffer, + rest: string[] + ) { + this.origin = origin; + this.logSize = logSize; + this.logHash = logHash; + this.rest = rest; + } + + static fromString(note: string): LogCheckpoint { + const lines = note.trim().split('\n'); + + if (lines.length < 4) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'too few lines in checkpoint header', + }); + } + + const origin = lines[0]; + const logSize = BigInt(lines[1]); + const rootHash = Buffer.from(lines[2], 'base64'); + const rest = lines.slice(3); + + return new LogCheckpoint(origin, logSize, rootHash, rest); + } +} diff --git a/packages/verify/src/timestamp/index.ts b/packages/verify/src/timestamp/index.ts new file mode 100644 index 00000000..8ce9efd7 --- /dev/null +++ b/packages/verify/src/timestamp/index.ts @@ -0,0 +1,92 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import assert from 'assert'; +import { VerificationError } from '../error'; +import { verifyCheckpoint } from './checkpoint'; +import { verifyMerkleInclusion } from './merkle'; +import { verifyTLogSET } from './set'; + +import type { + TLogEntryWithInclusionPromise, + TLogEntryWithInclusionProof, + TransparencyLogEntry, +} from '@sigstore/bundle'; +import type { RFC3161Timestamp } from '../shared.types'; +import type { CertAuthority, TLogAuthority } from '../trust'; + +export type TimestampType = 'transparency-log' | 'timestamp-authority'; + +export type TimestampVerificationResult = { + type: TimestampType; + logID: Buffer; + timestamp: Date; +}; + +/* istanbul ignore next */ +export function verifyTSATimestamp( + timestamp: RFC3161Timestamp, + timestampAuthorities: CertAuthority[] +): TimestampVerificationResult { + assert(timestamp); + assert(timestampAuthorities); + throw new VerificationError({ + code: 'NOT_IMPLEMENTED_ERROR', + message: 'timestamp-authority not implemented', + }); +} + +export function verifyTLogTimestamp( + entry: TransparencyLogEntry, + tlogAuthorities: TLogAuthority[] +): TimestampVerificationResult { + let inclusionVerified = false; + + if (isTLogEntryWithInclusionPromise(entry)) { + verifyTLogSET(entry, tlogAuthorities); + inclusionVerified = true; + } + + if (isTLogEntryWithInclusionProof(entry)) { + verifyMerkleInclusion(entry); + verifyCheckpoint(entry, tlogAuthorities); + inclusionVerified = true; + } + + if (!inclusionVerified) { + throw new VerificationError({ + code: 'TLOG_MISSING_INCLUSION_ERROR', + message: 'inclusion could not be verified', + }); + } + + return { + type: 'transparency-log', + logID: entry.logId.keyId, + timestamp: new Date(Number(entry.integratedTime) * 1000), + }; +} + +function isTLogEntryWithInclusionPromise( + entry: TransparencyLogEntry +): entry is TLogEntryWithInclusionPromise { + return entry.inclusionPromise !== undefined; +} + +function isTLogEntryWithInclusionProof( + entry: TransparencyLogEntry +): entry is TLogEntryWithInclusionProof { + return entry.inclusionProof !== undefined; +} diff --git a/packages/verify/src/timestamp/merkle.ts b/packages/verify/src/timestamp/merkle.ts new file mode 100644 index 00000000..041e3a3b --- /dev/null +++ b/packages/verify/src/timestamp/merkle.ts @@ -0,0 +1,127 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto } from '@sigstore/core'; +import { VerificationError } from '../error'; + +import type { TLogEntryWithInclusionProof } from '@sigstore/bundle'; + +const RFC6962_LEAF_HASH_PREFIX = Buffer.from([0x00]); +const RFC6962_NODE_HASH_PREFIX = Buffer.from([0x01]); + +export function verifyMerkleInclusion( + entry: TLogEntryWithInclusionProof +): void { + const inclusionProof = entry.inclusionProof; + const logIndex = BigInt(inclusionProof.logIndex); + const treeSize = BigInt(inclusionProof.treeSize); + + if (logIndex < 0n || logIndex >= treeSize) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: `invalid index: ${logIndex}`, + }); + } + + // Figure out which subset of hashes corresponds to the inner and border + // nodes + const { inner, border } = decompInclProof(logIndex, treeSize); + + if (inclusionProof.hashes.length !== inner + border) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'invalid hash count', + }); + } + + const innerHashes = inclusionProof.hashes.slice(0, inner); + const borderHashes = inclusionProof.hashes.slice(inner); + + // The entry's hash is the leaf hash + const leafHash = hashLeaf(entry.canonicalizedBody); + + // Chain the hashes belonging to the inner and border portions + const calculatedHash = chainBorderRight( + chainInner(leafHash, innerHashes, logIndex), + borderHashes + ); + + // Calculated hash should match the root hash in the inclusion proof + if (!crypto.bufferEqual(calculatedHash, inclusionProof.rootHash)) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROOF_ERROR', + message: 'calculated root hash does not match inclusion proof', + }); + } +} + +// Breaks down inclusion proof for a leaf at the specified index in a tree of +// the specified size. The split point is where paths to the index leaf and +// the (size - 1) leaf diverge. Returns lengths of the bottom and upper proof +// parts. +function decompInclProof( + index: bigint, + size: bigint +): { inner: number; border: number } { + const inner = innerProofSize(index, size); + const border = onesCount(index >> BigInt(inner)); + return { inner, border }; +} + +// Computes a subtree hash for a node on or below the tree's right border. +// Assumes the provided proof hashes are ordered from lower to higher levels +// and seed is the initial hash of the node specified by the index. +function chainInner(seed: Buffer, hashes: Buffer[], index: bigint): Buffer { + return hashes.reduce((acc, h, i) => { + if ((index >> BigInt(i)) & BigInt(1)) { + return hashChildren(h, acc); + } else { + return hashChildren(acc, h); + } + }, seed); +} + +// Computes a subtree hash for nodes along the tree's right border. +function chainBorderRight(seed: Buffer, hashes: Buffer[]): Buffer { + return hashes.reduce((acc, h) => hashChildren(h, acc), seed); +} + +function innerProofSize(index: bigint, size: bigint): number { + return bitLength(index ^ (size - BigInt(1))); +} + +// Counts the number of ones in the binary representation of the given number. +// https://en.wikipedia.org/wiki/Hamming_weight +function onesCount(num: bigint): number { + return num.toString(2).split('1').length - 1; +} + +// Returns the number of bits necessary to represent an integer in binary. +function bitLength(n: bigint): number { + if (n === 0n) { + return 0; + } + return n.toString(2).length; +} + +// Hashing logic according to RFC6962. +// https://datatracker.ietf.org/doc/html/rfc6962#section-2 +function hashChildren(left: Buffer, right: Buffer): Buffer { + return crypto.hash(RFC6962_NODE_HASH_PREFIX, left, right); +} + +function hashLeaf(leaf: Buffer): Buffer { + return crypto.hash(RFC6962_LEAF_HASH_PREFIX, leaf); +} diff --git a/packages/verify/src/timestamp/set.ts b/packages/verify/src/timestamp/set.ts new file mode 100644 index 00000000..d48f61d9 --- /dev/null +++ b/packages/verify/src/timestamp/set.ts @@ -0,0 +1,80 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { crypto, json } from '@sigstore/core'; +import { VerificationError } from '../error'; +import { TLogAuthority, filterTLogAuthorities } from '../trust'; + +import type { TLogEntryWithInclusionPromise } from '@sigstore/bundle'; + +// Structure over which the tlog SET signature is generated +interface VerificationPayload { + body: string; + integratedTime: number; + logIndex: number; + logID: string; +} + +// Verifies the SET for the given entry against the list of trusted +// transparency logs. Returns true if the SET can be verified against at least +// one of the trusted logs; otherwise, returns false. +export function verifyTLogSET( + entry: TLogEntryWithInclusionPromise, + tlogs: TLogAuthority[] +): void { + // Filter the list of tlog instances to only those which might be able to + // verify the SET + const validTLogs = filterTLogAuthorities(tlogs, { + logID: entry.logId.keyId, + targetDate: new Date(Number(entry.integratedTime) * 1000), + }); + + // Check to see if we can verify the SET against any of the valid tlogs + const verified = validTLogs.some((tlog) => { + // Re-create the original Rekor verification payload + const payload = toVerificationPayload(entry); + + // Canonicalize the payload and turn into a buffer for verification + const data = Buffer.from(json.canonicalize(payload), 'utf8'); + + // Extract the SET from the tlog entry + const signature = entry.inclusionPromise.signedEntryTimestamp; + + return crypto.verify(data, tlog.publicKey, signature); + }); + + if (!verified) { + throw new VerificationError({ + code: 'TLOG_INCLUSION_PROMISE_ERROR', + message: 'inclusion promise could not be verified', + }); + } +} + +// Returns a properly formatted "VerificationPayload" for one of the +// transaction log entires in the given bundle which can be used for SET +// verification. +function toVerificationPayload( + entry: TLogEntryWithInclusionPromise +): VerificationPayload { + const { integratedTime, logIndex, logId, canonicalizedBody } = entry; + + return { + body: canonicalizedBody.toString('base64'), + integratedTime: Number(integratedTime), + logIndex: Number(logIndex), + logID: logId.keyId.toString('hex'), + }; +} diff --git a/packages/verify/src/tlog/dsse.ts b/packages/verify/src/tlog/dsse.ts new file mode 100644 index 00000000..0360b2f6 --- /dev/null +++ b/packages/verify/src/tlog/dsse.ts @@ -0,0 +1,69 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { VerificationError } from '../error'; + +import type { ProposedDSSEEntry } from '@sigstore/rekor-types'; +import type { SignatureContent } from '../shared.types'; + +// Compare the given intoto tlog entry to the given bundle +export function verifyDSSETLogBody( + tlogEntry: ProposedDSSEEntry, + content: SignatureContent +): void { + switch (tlogEntry.apiVersion) { + case '0.0.1': + return verifyDSSE001TLogBody(tlogEntry, content); + default: + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: `unsupported dsse version: ${tlogEntry.apiVersion}`, + }); + } +} + +// Compare the given dsse v0.0.1 tlog entry to the given DSSE envelope. +function verifyDSSE001TLogBody( + tlogEntry: Extract, + content: SignatureContent +): void { + // Ensure the bundle's DSSE only contains a single signature + if (tlogEntry.spec.signatures?.length !== 1) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'signature count mismatch', + }); + } + + const tlogSig = tlogEntry.spec.signatures[0].signature; + + // Ensure that the signature in the bundle's DSSE matches tlog entry + if (!content.compareSignature(Buffer.from(tlogSig, 'base64'))) + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'tlog entry signature mismatch', + }); + + // Ensure the digest of the bundle's DSSE payload matches the digest in the + // tlog entry + const tlogHash = tlogEntry.spec.payloadHash?.value || ''; + + if (!content.compareDigest(Buffer.from(tlogHash, 'hex'))) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'DSSE payload hash mismatch', + }); + } +} diff --git a/packages/verify/src/tlog/hashedrekord.ts b/packages/verify/src/tlog/hashedrekord.ts new file mode 100644 index 00000000..bee3e822 --- /dev/null +++ b/packages/verify/src/tlog/hashedrekord.ts @@ -0,0 +1,62 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { VerificationError } from '../error'; + +import type { ProposedHashedRekordEntry } from '@sigstore/rekor-types'; +import type { SignatureContent } from '../shared.types'; + +// Compare the given hashedrekord tlog entry to the given bundle +export function verifyHashedRekordTLogBody( + tlogEntry: ProposedHashedRekordEntry, + content: SignatureContent +): void { + switch (tlogEntry.apiVersion) { + case '0.0.1': + return verifyHashedrekord001TLogBody(tlogEntry, content); + default: + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: `unsupported hashedrekord version: ${tlogEntry.apiVersion}`, + }); + } +} + +// Compare the given hashedrekord v0.0.1 tlog entry to the given message +// signature +function verifyHashedrekord001TLogBody( + tlogEntry: Extract, + content: SignatureContent +): void { + // Ensure that the bundles message signature matches the tlog entry + const tlogSig = tlogEntry.spec.signature.content || ''; + + if (!content.compareSignature(Buffer.from(tlogSig, 'base64'))) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'signature mismatch', + }); + } + + // Ensure that the bundle's message digest matches the tlog entry + const tlogDigest = tlogEntry.spec.data.hash?.value || ''; + + if (!content.compareDigest(Buffer.from(tlogDigest, 'hex'))) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'digest mismatch', + }); + } +} diff --git a/packages/verify/src/tlog/index.ts b/packages/verify/src/tlog/index.ts new file mode 100644 index 00000000..7194d40e --- /dev/null +++ b/packages/verify/src/tlog/index.ts @@ -0,0 +1,56 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { VerificationError } from '../error'; +import { verifyDSSETLogBody } from './dsse'; +import { verifyHashedRekordTLogBody } from './hashedrekord'; +import { verifyIntotoTLogBody } from './intoto'; + +import type { TransparencyLogEntry } from '@sigstore/bundle'; +import type { ProposedEntry } from '@sigstore/rekor-types'; +import type { SignatureContent } from '../shared.types'; + +// Verifies that the given tlog entry matches the supplied signature content. +export function verifyTLogBody( + entry: TransparencyLogEntry, + sigContent: SignatureContent +): void { + const { kind, version } = entry.kindVersion; + const body: ProposedEntry = JSON.parse( + entry.canonicalizedBody.toString('utf8') + ); + + if (kind !== body.kind || version !== body.apiVersion) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: `kind/version mismatch - expected: ${kind}/${version}, received: ${body.kind}/${body.apiVersion}`, + }); + } + + switch (body.kind) { + case 'dsse': + return verifyDSSETLogBody(body, sigContent); + case 'intoto': + return verifyIntotoTLogBody(body, sigContent); + case 'hashedrekord': + return verifyHashedRekordTLogBody(body, sigContent); + /* istanbul ignore next */ + default: + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: `unsupported kind: ${kind}`, + }); + } +} diff --git a/packages/verify/src/tlog/intoto.ts b/packages/verify/src/tlog/intoto.ts new file mode 100644 index 00000000..6a646dc9 --- /dev/null +++ b/packages/verify/src/tlog/intoto.ts @@ -0,0 +1,76 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { VerificationError } from '../error'; + +import type { ProposedIntotoEntry } from '@sigstore/rekor-types'; +import type { SignatureContent } from '../shared.types'; + +// Compare the given intoto tlog entry to the given bundle +export function verifyIntotoTLogBody( + tlogEntry: ProposedIntotoEntry, + content: SignatureContent +): void { + switch (tlogEntry.apiVersion) { + case '0.0.2': + return verifyIntoto002TLogBody(tlogEntry, content); + default: + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: `unsupported intoto version: ${tlogEntry.apiVersion}`, + }); + } +} + +// Compare the given intoto v0.0.2 tlog entry to the given DSSE envelope. +function verifyIntoto002TLogBody( + tlogEntry: Extract, + content: SignatureContent +): void { + // Ensure the bundle's DSSE contains a single signature + if (tlogEntry.spec.content.envelope.signatures?.length !== 1) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'signature count mismatch', + }); + } + + // Signature is double-base64-encoded in the tlog entry + const tlogSig = base64Decode( + tlogEntry.spec.content.envelope.signatures[0].sig + ); + + // Ensure that the signature in the bundle's DSSE matches tlog entry + if (!content.compareSignature(Buffer.from(tlogSig, 'base64'))) + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'tlog entry signature mismatch', + }); + + // Ensure the digest of the bundle's DSSE payload matches the digest in the + // tlog entry + const tlogHash = tlogEntry.spec.content.payloadHash?.value || ''; + + if (!content.compareDigest(Buffer.from(tlogHash, 'hex'))) { + throw new VerificationError({ + code: 'TLOG_BODY_ERROR', + message: 'DSSE payload hash mismatch', + }); + } +} + +function base64Decode(str: string): string { + return Buffer.from(str, 'base64').toString('utf-8'); +} diff --git a/packages/verify/src/trust/filter.ts b/packages/verify/src/trust/filter.ts new file mode 100644 index 00000000..8e087b60 --- /dev/null +++ b/packages/verify/src/trust/filter.ts @@ -0,0 +1,58 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import type { CertAuthority, TLogAuthority } from './trust.types'; + +type CertAuthorityFilterCriteria = { + start: Date; + end: Date; +}; + +export function filterCertAuthorities( + certAuthorities: CertAuthority[], + criteria: CertAuthorityFilterCriteria +): CertAuthority[] { + return certAuthorities.filter((ca) => { + return ( + ca.validFor.start <= criteria.start && ca.validFor.end >= criteria.end + ); + }); +} + +type TLogAuthorityFilterCriteria = { + targetDate: Date; + logID?: Buffer; +}; + +// Filter the list of tlog instances to only those which match the given log +// ID and have public keys which are valid for the given integrated time. +export function filterTLogAuthorities( + tlogAuthorities: TLogAuthority[], + criteria: TLogAuthorityFilterCriteria +): TLogAuthority[] { + return tlogAuthorities.filter((tlog) => { + // If we're filtering by log ID and the log IDs don't match, we can't use + // this tlog + if (criteria.logID && !tlog.logID.equals(criteria.logID)) { + return false; + } + + // Check that the integrated time is within the validFor range + return ( + tlog.validFor.start <= criteria.targetDate && + criteria.targetDate <= tlog.validFor.end + ); + }); +} diff --git a/packages/verify/src/trust/index.ts b/packages/verify/src/trust/index.ts new file mode 100644 index 00000000..2d57cefd --- /dev/null +++ b/packages/verify/src/trust/index.ts @@ -0,0 +1,103 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { X509Certificate, crypto } from '@sigstore/core'; + +import type { + CertificateAuthority, + PublicKey, + TransparencyLogInstance, + TrustedRoot, +} from '@sigstore/protobuf-specs'; +import { VerificationError } from '../error'; +import type { + CertAuthority, + KeyFinderFunc, + TLogAuthority, + TrustMaterial, +} from './trust.types'; + +const BEGINNING_OF_TIME = new Date(0); +const END_OF_TIME = new Date(8640000000000000); + +export { filterCertAuthorities, filterTLogAuthorities } from './filter'; + +export type { + CertAuthority, + TLogAuthority, + TrustMaterial, +} from './trust.types'; + +export function toTrustMaterial( + root: TrustedRoot, + keys?: Record +): TrustMaterial { + return { + certificateAuthorities: + root.certificateAuthorities.map(createCertAuthority), + timestampAuthorities: root.timestampAuthorities.map(createCertAuthority), + tlogs: root.tlogs.map(createTLogAuthority), + ctlogs: root.ctlogs.map(createTLogAuthority), + publicKey: keyLocator(keys), + }; +} + +function createTLogAuthority( + tlogInstance: TransparencyLogInstance +): TLogAuthority { + return { + logID: tlogInstance.logId!.keyId, + publicKey: crypto.createPublicKey(tlogInstance.publicKey!.rawBytes!), + validFor: { + start: tlogInstance.publicKey!.validFor?.start || BEGINNING_OF_TIME, + end: tlogInstance.publicKey!.validFor?.end || END_OF_TIME, + }, + }; +} + +function createCertAuthority(ca: CertificateAuthority): CertAuthority { + return { + certChain: ca.certChain!.certificates.map((cert) => { + return X509Certificate.parse(cert.rawBytes); + }), + validFor: { + start: ca.validFor?.start || BEGINNING_OF_TIME, + end: ca.validFor?.end || END_OF_TIME, + }, + }; +} + +function keyLocator(keys?: Record): KeyFinderFunc { + return (hint: string) => { + const key = (keys || {})[hint]; + + if (!key) { + throw new VerificationError({ + code: 'PUBLIC_KEY_ERROR', + message: `key not found: ${hint}`, + }); + } + + return { + publicKey: crypto.createPublicKey(key.rawBytes!), + validFor: (date: Date) => { + return ( + (key.validFor?.start || BEGINNING_OF_TIME) <= date && + (key.validFor?.end || END_OF_TIME) >= date + ); + }, + }; + }; +} diff --git a/packages/verify/src/trust/trust.types.ts b/packages/verify/src/trust/trust.types.ts new file mode 100644 index 00000000..25b0db8b --- /dev/null +++ b/packages/verify/src/trust/trust.types.ts @@ -0,0 +1,48 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import type { X509Certificate, crypto } from '@sigstore/core'; + +export type TLogAuthority = { + logID: Buffer; + publicKey: crypto.KeyObject; + validFor: { + start: Date; + end: Date; + }; +}; + +export type CertAuthority = { + certChain: X509Certificate[]; + validFor: { + start: Date; + end: Date; + }; +}; + +export type TimeConstrainedKey = { + publicKey: crypto.KeyObject; + validFor(date: Date): boolean; +}; + +export type KeyFinderFunc = (hint: string) => TimeConstrainedKey; + +export type TrustMaterial = { + certificateAuthorities: CertAuthority[]; + timestampAuthorities: CertAuthority[]; + tlogs: TLogAuthority[]; + ctlogs: TLogAuthority[]; + publicKey: KeyFinderFunc; +}; diff --git a/packages/verify/src/verifier.ts b/packages/verify/src/verifier.ts new file mode 100644 index 00000000..3fd388ff --- /dev/null +++ b/packages/verify/src/verifier.ts @@ -0,0 +1,162 @@ +/* +Copyright 2023 The Sigstore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import { isDeepStrictEqual } from 'util'; +import { VerificationError } from './error'; +import { verifyCertificate, verifyPublicKey } from './key'; +import { verifyTLogTimestamp, verifyTSATimestamp } from './timestamp'; +import { verifyTLogBody } from './tlog'; + +import type { SignedEntity, Signer } from './shared.types'; +import type { TrustMaterial } from './trust'; + +export type VerifierOptions = { + tlogThreshold?: number; + ctlogThreshold?: number; + tsaThreshold?: number; +}; + +export class Verifier { + private trustMaterial: TrustMaterial; + private options: Required; + + constructor(trustMaterial: TrustMaterial, options: VerifierOptions = {}) { + this.trustMaterial = trustMaterial; + this.options = { + ctlogThreshold: options.ctlogThreshold ?? 1, + tlogThreshold: options.tlogThreshold ?? 1, + tsaThreshold: options.tsaThreshold ?? 0, + }; + } + + public verify(entity: SignedEntity): Signer { + const timestamps = this.verifyTimestamps(entity); + const signer = this.verifySigningKey(entity, timestamps); + this.verifyTLogs(entity); + this.verifySignature(entity, signer); + return signer; + } + + // Checks that all of the timestamps in the entity are valid and returns them + private verifyTimestamps(entity: SignedEntity): Date[] { + let tlogCount = 0; + let tsaCount = 0; + + const timestamps = entity.timestamps.map((timestamp) => { + switch (timestamp.$case) { + /* istanbul ignore next - implement this*/ + case 'timestamp-authority': + tsaCount++; + return verifyTSATimestamp( + timestamp.timestamp, + this.trustMaterial.timestampAuthorities + ); + case 'transparency-log': + tlogCount++; + return verifyTLogTimestamp( + timestamp.tlogEntry, + this.trustMaterial.tlogs + ); + } + }); + + // Check for duplicate timestamps + if (containsDupes(timestamps)) { + throw new VerificationError({ + code: 'TIMESTAMP_ERROR', + message: 'duplicate timestamp', + }); + } + + if (tlogCount < this.options.tlogThreshold) { + throw new VerificationError({ + code: 'TIMESTAMP_ERROR', + message: `expected ${this.options.tlogThreshold} tlog timestamps, got ${tlogCount}`, + }); + } + + if (tsaCount < this.options.tsaThreshold) { + throw new VerificationError({ + code: 'TIMESTAMP_ERROR', + message: `expected ${this.options.tsaThreshold} tsa timestamps, got ${tsaCount}`, + }); + } + + return timestamps.map((t) => t.timestamp); + } + + // Checks that the signing key is valid for all of the the supplied timestamps + // and returns the signer. + private verifySigningKey({ key }: SignedEntity, timestamps: Date[]): Signer { + switch (key.$case) { + case 'public-key': { + return verifyPublicKey(key.hint, timestamps, this.trustMaterial); + } + case 'certificate': { + const result = verifyCertificate( + key.certificate, + timestamps, + this.trustMaterial + ); + + /* istanbul ignore next - no fixture */ + if (containsDupes(result.scts)) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: 'duplicate SCT', + }); + } + + if (result.scts.length < this.options.ctlogThreshold) { + throw new VerificationError({ + code: 'CERTIFICATE_ERROR', + message: `expected ${this.options.ctlogThreshold} SCTs, got ${result.scts.length}`, + }); + } + + return result.signer; + } + } + } + + // Checks that the tlog entries are valid for the supplied content + private verifyTLogs({ signature: content, tlogEntries }: SignedEntity): void { + tlogEntries.forEach((entry) => verifyTLogBody(entry, content)); + } + + // Checks that the signature is valid for the supplied content + private verifySignature(entity: SignedEntity, signer: Signer): void { + if (!entity.signature.verifySignature(signer.key)) { + throw new VerificationError({ + code: 'SIGNATURE_ERROR', + message: 'signature verification failed', + }); + } + } +} + +// Checks for duplicate items in the array. Objects are compared using +// deep equality. +function containsDupes(arr: unknown[]): boolean { + for (let i = 0; i < arr.length; i++) { + for (let j = i + 1; j < arr.length; j++) { + if (isDeepStrictEqual(arr[i], arr[j])) { + return true; + } + } + } + + return false; +} diff --git a/packages/verify/tsconfig.json b/packages/verify/tsconfig.json new file mode 100644 index 00000000..692598ff --- /dev/null +++ b/packages/verify/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "composite": true + }, + "exclude": [ + "./dist", + "**/__tests__", + "jest.setup.ts" + ], + "references": [ + { "path": "../bundle" }, + { "path": "../core" } + ] +}