From 76ebacef7032a2dc68dab429bd6c5ab34e832813 Mon Sep 17 00:00:00 2001 From: Bob Callaway Date: Sun, 28 May 2023 20:16:41 -0400 Subject: [PATCH 1/4] Adds support for new Rekor 'dsse' entry type Signed-off-by: Bob Callaway --- .changeset/little-houses-complain.md | 5 ++ .../client/src/__tests__/tlog/format.test.ts | 82 +++++++++++++++++- packages/client/src/external/rekor.ts | 2 + packages/client/src/tlog/format.ts | 58 +++++++++++-- packages/client/src/tlog/verify/body.ts | 64 ++++++++++++++ .../rekor-types/hack/generate-rekor-types | 4 +- .../rekor-types/src/__generated__/dsse.ts | 86 +++++++++++++++++++ .../rekor-types/src/__generated__/index.ts | 2 +- .../rekor-types/src/__generated__/intoto.ts | 14 +-- .../src/__generated__/models/RekorVersion.ts | 11 --- .../src/__generated__/models/dsse.ts | 11 +++ packages/rekor-types/src/index.ts | 14 ++- 12 files changed, 319 insertions(+), 34 deletions(-) create mode 100644 .changeset/little-houses-complain.md create mode 100644 packages/rekor-types/src/__generated__/dsse.ts delete mode 100644 packages/rekor-types/src/__generated__/models/RekorVersion.ts create mode 100644 packages/rekor-types/src/__generated__/models/dsse.ts diff --git a/.changeset/little-houses-complain.md b/.changeset/little-houses-complain.md new file mode 100644 index 00000000..397b9378 --- /dev/null +++ b/.changeset/little-houses-complain.md @@ -0,0 +1,5 @@ +--- +'@sigstore/rekor-types': minor +--- + +add DSSE type diff --git a/packages/client/src/__tests__/tlog/format.test.ts b/packages/client/src/__tests__/tlog/format.test.ts index ae7f1568..f3cfdf0d 100644 --- a/packages/client/src/__tests__/tlog/format.test.ts +++ b/packages/client/src/__tests__/tlog/format.test.ts @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import { + toProposedDSSEEntry, toProposedHashedRekordEntry, toProposedIntotoEntry, } from '../../tlog/format'; @@ -55,6 +56,81 @@ describe('format', () => { }); }); + describe('toProposedDSSEEntry', () => { + describe('when there is a single signature in the envelope', () => { + const envelope: Envelope = { + payloadType: 'application/vnd.in-toto+json', + payload: Buffer.from('payload'), + signatures: [{ keyid: '123', sig: signature }], + }; + + it('returns a valid dsse entry', () => { + const entry = toProposedDSSEEntry(envelope, sigMaterial); + + expect(entry.apiVersion).toEqual('0.0.1'); + expect(entry.kind).toEqual('dsse'); + expect(entry.spec).toBeTruthy(); + expect(entry.spec.proposedContent).toBeTruthy(); + expect(typeof entry.spec.proposedContent?.envelope).toBe('string'); + expect(entry.spec.proposedContent?.verifiers).toHaveLength(1); + expect(entry.spec.proposedContent?.verifiers[0]).toEqual( + enc.base64Encode(cert) + ); + + // ensure we have the expected JSON object stored in the string + if (typeof entry.spec.proposedContent?.envelope === 'string') { + const envObj = JSON.parse(entry.spec.proposedContent?.envelope); + expect(envObj).toEqual(Envelope.toJSON(envelope)); + // ensure we only have 1 signature specified in the object + expect(envObj.signatures).toHaveLength(1); + } else { + fail('dsse envelope should be set as JSON string'); + } + + // we don't want the persisted properties to show up in a proposed entry + expect(entry.spec.signatures).toBeUndefined(); + }); + }); + + describe('when there are multiple signatures in the envelope', () => { + const envelope: Envelope = { + payloadType: 'application/vnd.in-toto+json', + payload: Buffer.from('payload'), + signatures: [ + { keyid: '123', sig: signature }, + { keyid: '456', sig: signature }, + ], + }; + + it('returns a valid dsse entry', () => { + const entry = toProposedDSSEEntry(envelope, sigMaterial); + + expect(entry.apiVersion).toEqual('0.0.1'); + expect(entry.kind).toEqual('dsse'); + expect(entry.spec).toBeTruthy(); + expect(entry.spec.proposedContent).toBeTruthy(); + expect(typeof entry.spec.proposedContent?.envelope).toBe('string'); + expect(entry.spec.proposedContent?.verifiers).toHaveLength(1); + expect(entry.spec.proposedContent?.verifiers[0]).toEqual( + enc.base64Encode(cert) + ); + + // ensure we have the expected JSON object stored in the string + if (typeof entry.spec.proposedContent?.envelope === 'string') { + const envObj = JSON.parse(entry.spec.proposedContent?.envelope); + expect(envObj).toEqual(Envelope.toJSON(envelope)); + // ensure we have 2 signatures specified in the object + expect(envObj.signatures).toHaveLength(2); + } else { + fail('dsse envelope should be set as JSON string'); + } + + // we don't want the persisted properties to show up in a proposed entry + expect(entry.spec.signatures).toBeUndefined(); + }); + }); + }); + describe('toProposedIntotoEntry', () => { describe('when the keyid is a non-empty string', () => { const envelope: Envelope = { @@ -99,7 +175,7 @@ describe('format', () => { // This hard-coded hash value helps us detect if we've unintentionally // changed the hashing algorithm. expect(entry.spec.content.hash?.value).toBe( - '91a5eb7452452720d704da5442acb9703252b3ab7be51ec155a244f5c9aa5ec8' + '37d47ab456ca63a84f6457be655dd49799542f2e1db5d05160b214fb0b9a7f55' ); }); }); @@ -129,7 +205,7 @@ describe('format', () => { // This hard-coded hash value helps us detect if we've unintentionally // changed the hashing algorithm. expect(entry.spec.content.hash?.value).toBe( - '295fd391f3b3f349cdaa686befaa765d90c0b411a0811e45f8bc481338a51622' + 'f39ab279af9d9be421342ce4c8e5c422b5bc3dd20602703b1893283a934fbe72' ); }); }); @@ -163,7 +239,7 @@ describe('format', () => { // This hard-coded hash value helps us detect if we've unintentionally // changed the hashing algorithm. expect(entry.spec.content.hash?.value).toBe( - '91a5eb7452452720d704da5442acb9703252b3ab7be51ec155a244f5c9aa5ec8' + '37d47ab456ca63a84f6457be655dd49799542f2e1db5d05160b214fb0b9a7f55' ); }); }); diff --git a/packages/client/src/external/rekor.ts b/packages/client/src/external/rekor.ts index 62c96dcd..4e6b2771 100644 --- a/packages/client/src/external/rekor.ts +++ b/packages/client/src/external/rekor.ts @@ -20,6 +20,7 @@ import { checkStatus } from './error'; import type { LogEntry, ProposedEntry, + ProposedDSSEEntry, ProposedHashedRekordEntry, ProposedIntotoEntry, SearchIndex, @@ -31,6 +32,7 @@ export type { ProposedEntry, SearchIndex, SearchLogQuery, + ProposedDSSEEntry, ProposedHashedRekordEntry, ProposedIntotoEntry, }; diff --git a/packages/client/src/tlog/format.ts b/packages/client/src/tlog/format.ts index 709d148a..440b3163 100644 --- a/packages/client/src/tlog/format.ts +++ b/packages/client/src/tlog/format.ts @@ -18,13 +18,30 @@ import { Envelope } from '../types/sigstore'; import { crypto, encoding as enc, json } from '../util'; import type { + ProposedDSSEEntry, ProposedHashedRekordEntry, ProposedIntotoEntry, } from '../external/rekor'; +const DEFAULT_DSSE_API_VERSION = '0.0.1'; const DEFAULT_HASHEDREKORD_API_VERSION = '0.0.1'; const DEFAULT_INTOTO_API_VERSION = '0.0.2'; +// Returns a properly formatted Rekor "dsse" entry for the given DSSE +// envelope and signature +export function toProposedDSSEEntry( + envelope: Envelope, + signature: SignatureMaterial, + apiVersion = DEFAULT_DSSE_API_VERSION +): ProposedDSSEEntry { + switch (apiVersion) { + case '0.0.1': + return toProposedDSSEV001Entry(envelope, signature); + default: + throw new Error(`Unsupported dsse kind API version: ${apiVersion}`); + } +} + // Returns a properly formatted Rekor "hashedrekord" entry for the given digest // and signature export function toProposedHashedRekordEntry( @@ -69,6 +86,21 @@ export function toProposedIntotoEntry( throw new Error(`Unsupported intoto kind API version: ${apiVersion}`); } } +function toProposedDSSEV001Entry( + envelope: Envelope, + signature: SignatureMaterial +): ProposedDSSEEntry { + return { + apiVersion: '0.0.1', + kind: 'dsse', + spec: { + proposedContent: { + envelope: JSON.stringify(Envelope.toJSON(envelope)), + verifiers: [enc.base64Encode(toPublicKey(signature))], + }, + }, + }; +} function toProposedIntotoV002Entry( envelope: Envelope, @@ -78,7 +110,7 @@ function toProposedIntotoV002Entry( const payloadHash = crypto.hash(envelope.payload).toString('hex'); // Calculate the value for the hash field in the Rekor entry - const envelopeHash = calculateDSSEHash(envelope); + const envelopeHash = calculateDSSEHash(envelope, signature); // Collect values for re-creating the DSSE envelope. // Double-encode payload and signature cause that's what Rekor expects @@ -90,7 +122,7 @@ function toProposedIntotoV002Entry( // Create the envelope portion of the entry. Note the inclusion of the // publicKey in the signature struct is not a standard part of a DSSE // envelope, but is required by Rekor. - const dsse: ProposedIntotoEntry['spec']['content']['envelope'] = { + const dsseEnv: ProposedIntotoEntry['spec']['content']['envelope'] = { payloadType: envelope.payloadType, payload: payload, signatures: [{ sig, publicKey }], @@ -100,7 +132,7 @@ function toProposedIntotoV002Entry( // need to do the same here so that we can properly recreate the entry for // verification. if (keyid.length > 0) { - dsse.signatures[0].keyid = keyid; + dsseEnv.signatures[0].keyid = keyid; } return { @@ -108,7 +140,7 @@ function toProposedIntotoV002Entry( kind: 'intoto', spec: { content: { - envelope: dsse, + envelope: dsseEnv, hash: { algorithm: 'sha256', value: envelopeHash }, payloadHash: { algorithm: 'sha256', value: payloadHash }, }, @@ -123,19 +155,27 @@ function toProposedIntotoV002Entry( // * signature is base64 encoded (only the first signature is used) // * keyid is included ONLY if it is NOT an empty string // * The resulting JSON is canonicalized and hashed to a hex string -function calculateDSSEHash(envelope: Envelope): string { - const dsse: ProposedIntotoEntry['spec']['content']['envelope'] = { +function calculateDSSEHash( + envelope: Envelope, + signature: SignatureMaterial +): string { + const dsseEnv: ProposedIntotoEntry['spec']['content']['envelope'] = { payloadType: envelope.payloadType, payload: envelope.payload.toString('base64'), - signatures: [{ sig: envelope.signatures[0].sig.toString('base64') }], + signatures: [ + { + sig: envelope.signatures[0].sig.toString('base64'), + publicKey: toPublicKey(signature), + }, + ], }; // If the keyid is an empty string, Rekor seems to remove it altogether. if (envelope.signatures[0].keyid.length > 0) { - dsse.signatures[0].keyid = envelope.signatures[0].keyid; + dsseEnv.signatures[0].keyid = envelope.signatures[0].keyid; } - return crypto.hash(json.canonicalize(dsse)).toString('hex'); + return crypto.hash(json.canonicalize(dsseEnv)).toString('hex'); } function toPublicKey(signature: SignatureMaterial): string { diff --git a/packages/client/src/tlog/verify/body.ts b/packages/client/src/tlog/verify/body.ts index 6c78cd19..cc8b0ea6 100644 --- a/packages/client/src/tlog/verify/body.ts +++ b/packages/client/src/tlog/verify/body.ts @@ -19,6 +19,7 @@ import { crypto, encoding as enc } from '../../util'; import type { ProposedEntry, + ProposedDSSEEntry, ProposedHashedRekordEntry, ProposedIntotoEntry, } from '../../external/rekor'; @@ -41,6 +42,9 @@ export function verifyTLogBody( } switch (body.kind) { + case 'dsse': + verifyDSSETLogBody(body, bundleContent); + break; case 'intoto': verifyIntotoTLogBody(body, bundleContent); break; @@ -56,6 +60,30 @@ export function verifyTLogBody( } } +// Compare the given intoto tlog entry to the given bundle +function verifyDSSETLogBody( + tlogEntry: ProposedDSSEEntry, + content: sigstore.Bundle['content'] +): void { + if (content?.$case !== 'dsseEnvelope') { + throw new VerificationError( + `unsupported bundle content: ${content?.$case || 'unknown'}` + ); + } + + const dsse = content.dsseEnvelope; + + switch (tlogEntry.apiVersion) { + case '0.0.1': + verifyDSSE001TLogBody(tlogEntry, dsse); + break; + default: + throw new VerificationError( + `unsupported dsse version: ${tlogEntry.apiVersion}` + ); + } +} + // Compare the given intoto tlog entry to the given bundle function verifyIntotoTLogBody( tlogEntry: ProposedIntotoEntry, @@ -104,6 +132,42 @@ function verifyHashedRekordTLogBody( } } +// Compare the given dsse v0.0.1 tlog entry to the given DSSE envelope. +function verifyDSSE001TLogBody( + tlogEntry: Extract, + dsse: sigstore.Envelope +): void { + // Collect all of the signatures from the DSSE envelope + // Turns them into base64-encoded strings for comparison + const dsseSigs = dsse.signatures.map((signature) => + signature.sig.toString('base64') + ); + + // Collect all of the signatures from the tlog entry + // Remember that tlog signatures are double base64-encoded + const tlogSigs = tlogEntry.spec.signatures?.map((signature) => + signature.signature ? enc.base64Decode(signature.signature) : '' + ); + + // Ensure the bundle's DSSE and the tlog entry contain the same number of signatures + if (dsseSigs.length !== tlogSigs?.length) { + throw new VerificationError(TLOG_MISMATCH_ERROR_MSG); + } + + // Ensure that every signature in the bundle's DSSE is present in the tlog entry + if (!dsseSigs.every((dsseSig) => tlogSigs.includes(dsseSig))) { + throw new VerificationError(TLOG_MISMATCH_ERROR_MSG); + } + + // Ensure the digest of the bundle's DSSE payload matches the digest in the + // tlog entry + const dssePayloadHash = crypto.hash(dsse.payload).toString('hex'); + + if (dssePayloadHash !== tlogEntry.spec.payloadHash?.value) { + throw new VerificationError(TLOG_MISMATCH_ERROR_MSG); + } +} + // Compare the given intoto v0.0.2 tlog entry to the given DSSE envelope. function verifyIntoto002TLogBody( tlogEntry: Extract, diff --git a/packages/rekor-types/hack/generate-rekor-types b/packages/rekor-types/hack/generate-rekor-types index 1999f814..c0603be7 100755 --- a/packages/rekor-types/hack/generate-rekor-types +++ b/packages/rekor-types/hack/generate-rekor-types @@ -2,7 +2,7 @@ set -ex # Check-out Rekor repo -REF=4bb6f441c1b27ccc7e625c721c7d3203acc7b313 +REF=576458cb53269ed54dccf8a43271ee02a785c191 REKOR_DIR=/tmp/rekor rm -rf ${REKOR_DIR} @@ -21,7 +21,7 @@ npx openapi --input "${REKOR_DIR}/openapi.yaml" \ --exportSchemas=false # Run json2ts on schemas -KINDS=( intoto hashedrekord ) +KINDS=( dsse intoto hashedrekord ) for KIND in "${KINDS[@]}" do TYPE_PATH=${REKOR_DIR}/pkg/types/${KIND} diff --git a/packages/rekor-types/src/__generated__/dsse.ts b/packages/rekor-types/src/__generated__/dsse.ts new file mode 100644 index 00000000..491eafcc --- /dev/null +++ b/packages/rekor-types/src/__generated__/dsse.ts @@ -0,0 +1,86 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * log entry schema for dsse envelopes + */ +export type DSSESchema = DSSEV001Schema; +/** + * Schema for DSSE envelopes + */ +export type DSSEV001Schema = DSSEV001Schema1 & DSSEV001Schema2; +export type DSSEV001Schema2 = { + [k: string]: unknown; +}; + +export interface DSSEV001Schema1 { + proposedContent?: { + /** + * DSSE envelope specified as a stringified JSON object + */ + envelope: string; + /** + * collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings + * + * @minItems 1 + */ + verifiers: [string, ...string[]]; + }; + /** + * extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings + * + * @minItems 1 + */ + signatures?: [ + { + /** + * base64 encoded signature of the payload + */ + signature: string; + /** + * verification material that was used to verify the corresponding signature, specified as a base64 encoded string + */ + verifier: string; + }, + ...{ + /** + * base64 encoded signature of the payload + */ + signature: string; + /** + * verification material that was used to verify the corresponding signature, specified as a base64 encoded string + */ + verifier: string; + }[] + ]; + /** + * Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor + */ + envelopeHash?: { + /** + * The hashing function used to compute the hash value + */ + algorithm: "sha256"; + /** + * The value of the computed digest over the entire envelope + */ + value: string; + }; + /** + * Specifies the hash algorithm and value covering the payload within the DSSE envelope + */ + payloadHash?: { + /** + * The hashing function used to compute the hash value + */ + algorithm: "sha256"; + /** + * The value of the computed digest over the payload within the envelope + */ + value: string; + }; +} diff --git a/packages/rekor-types/src/__generated__/index.ts b/packages/rekor-types/src/__generated__/index.ts index 49a7240a..69b86cfd 100644 --- a/packages/rekor-types/src/__generated__/index.ts +++ b/packages/rekor-types/src/__generated__/index.ts @@ -5,6 +5,7 @@ export type { alpine } from './models/alpine'; export type { ConsistencyProof } from './models/ConsistencyProof'; export type { cose } from './models/cose'; +export type { dsse } from './models/dsse'; export type { Error } from './models/Error'; export type { hashedrekord } from './models/hashedrekord'; export type { helm } from './models/helm'; @@ -16,7 +17,6 @@ export type { LogEntry } from './models/LogEntry'; export type { LogInfo } from './models/LogInfo'; export type { ProposedEntry } from './models/ProposedEntry'; export type { rekord } from './models/rekord'; -export type { RekorVersion } from './models/RekorVersion'; export type { rfc3161 } from './models/rfc3161'; export type { rpm } from './models/rpm'; export type { SearchIndex } from './models/SearchIndex'; diff --git a/packages/rekor-types/src/__generated__/intoto.ts b/packages/rekor-types/src/__generated__/intoto.ts index ed186b64..a81dcfcb 100644 --- a/packages/rekor-types/src/__generated__/intoto.ts +++ b/packages/rekor-types/src/__generated__/intoto.ts @@ -20,7 +20,7 @@ export interface IntotoV001Schema { */ envelope?: string; /** - * Specifies the hash algorithm and value encompassing the entire signed envelope + * Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored */ hash?: { /** @@ -33,7 +33,7 @@ export interface IntotoV001Schema { value: string; }; /** - * Specifies the hash algorithm and value covering the payload within the DSSE envelope + * Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored */ payloadHash?: { /** @@ -59,7 +59,7 @@ export interface IntotoV002Schema { /** * dsse envelope */ - envelope?: { + envelope: { /** * payload of the envelope */ @@ -82,11 +82,11 @@ export interface IntotoV002Schema { /** * signature of the payload */ - sig?: string; + sig: string; /** * public key that corresponds to this signature */ - publicKey?: string; + publicKey: string; }, ...{ /** @@ -96,11 +96,11 @@ export interface IntotoV002Schema { /** * signature of the payload */ - sig?: string; + sig: string; /** * public key that corresponds to this signature */ - publicKey?: string; + publicKey: string; }[] ]; }; diff --git a/packages/rekor-types/src/__generated__/models/RekorVersion.ts b/packages/rekor-types/src/__generated__/models/RekorVersion.ts deleted file mode 100644 index c7cb39a7..00000000 --- a/packages/rekor-types/src/__generated__/models/RekorVersion.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type RekorVersion = { - version: string; - commit: string; - treestate: string; - builddate: string; -}; - diff --git a/packages/rekor-types/src/__generated__/models/dsse.ts b/packages/rekor-types/src/__generated__/models/dsse.ts new file mode 100644 index 00000000..db5c0500 --- /dev/null +++ b/packages/rekor-types/src/__generated__/models/dsse.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { ProposedEntry } from './ProposedEntry'; + +/** + * DSSE envelope + */ +export type dsse = ProposedEntry; + diff --git a/packages/rekor-types/src/index.ts b/packages/rekor-types/src/index.ts index a6338e9a..0560073a 100644 --- a/packages/rekor-types/src/index.ts +++ b/packages/rekor-types/src/index.ts @@ -1,12 +1,20 @@ +import type { DSSEV001Schema } from './__generated__/dsse'; import type { HashedRekorV001Schema } from './__generated__/hashedrekord'; import type { IntotoV001Schema, IntotoV002Schema, } from './__generated__/intoto'; +const DSSE_KIND = 'dsse'; const INTOTO_KIND = 'intoto'; const HASHEDREKORD_KIND = 'hashedrekord'; +export type ProposedDSSEEntry = { + apiVersion: '0.0.1'; + kind: typeof DSSE_KIND; + spec: DSSEV001Schema; +}; + export type ProposedHashedRekordEntry = { apiVersion: '0.0.1'; kind: typeof HASHEDREKORD_KIND; @@ -25,7 +33,10 @@ export type ProposedIntotoEntry = spec: IntotoV002Schema; }; -export type ProposedEntry = ProposedHashedRekordEntry | ProposedIntotoEntry; +export type ProposedEntry = + | ProposedDSSEEntry + | ProposedHashedRekordEntry + | ProposedIntotoEntry; export type SearchLogQuery = { entryUUIDs?: Array; @@ -34,6 +45,7 @@ export type SearchLogQuery = { }; export type { InclusionProof, LogEntry, SearchIndex } from './__generated__/'; +export type { DSSEV001Schema } from './__generated__/dsse'; export type { HashedRekorV001Schema } from './__generated__/hashedrekord'; export type { IntotoV001Schema, From 8212d90b9228ba6f09cb099851e6405c2168afb3 Mon Sep 17 00:00:00 2001 From: Bob Callaway Date: Thu, 1 Jun 2023 13:33:07 -0400 Subject: [PATCH 2/4] fix comment and remove double encoding Signed-off-by: Bob Callaway --- packages/client/src/tlog/verify/body.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/client/src/tlog/verify/body.ts b/packages/client/src/tlog/verify/body.ts index cc8b0ea6..923791ae 100644 --- a/packages/client/src/tlog/verify/body.ts +++ b/packages/client/src/tlog/verify/body.ts @@ -144,9 +144,8 @@ function verifyDSSE001TLogBody( ); // Collect all of the signatures from the tlog entry - // Remember that tlog signatures are double base64-encoded const tlogSigs = tlogEntry.spec.signatures?.map((signature) => - signature.signature ? enc.base64Decode(signature.signature) : '' + signature.signature ); // Ensure the bundle's DSSE and the tlog entry contain the same number of signatures From 0522d4bbed372826040eb0ae448e279f00b8e123 Mon Sep 17 00:00:00 2001 From: Bob Callaway Date: Thu, 1 Jun 2023 13:38:15 -0400 Subject: [PATCH 3/4] fix lint complaint Signed-off-by: Bob Callaway --- packages/client/src/tlog/verify/body.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/tlog/verify/body.ts b/packages/client/src/tlog/verify/body.ts index 923791ae..065cb712 100644 --- a/packages/client/src/tlog/verify/body.ts +++ b/packages/client/src/tlog/verify/body.ts @@ -144,8 +144,8 @@ function verifyDSSE001TLogBody( ); // Collect all of the signatures from the tlog entry - const tlogSigs = tlogEntry.spec.signatures?.map((signature) => - signature.signature + const tlogSigs = tlogEntry.spec.signatures?.map( + (signature) => signature.signature ); // Ensure the bundle's DSSE and the tlog entry contain the same number of signatures From a59ac50bb717ec32f62ec129ae1b511fa8d6f6c7 Mon Sep 17 00:00:00 2001 From: Brian DeHamer Date: Mon, 5 Jun 2023 10:59:30 -0700 Subject: [PATCH 4/4] add test case for verifying DSSE tlog entry Signed-off-by: Brian DeHamer --- .../__tests__/__fixtures__/bundles/dsse.ts | 44 +++++++++++++++++++ .../src/__tests__/tlog/verify/body.test.ts | 14 ++++++ 2 files changed, 58 insertions(+) diff --git a/packages/client/src/__tests__/__fixtures__/bundles/dsse.ts b/packages/client/src/__tests__/__fixtures__/bundles/dsse.ts index 8f32b87e..c8052482 100644 --- a/packages/client/src/__tests__/__fixtures__/bundles/dsse.ts +++ b/packages/client/src/__tests__/__fixtures__/bundles/dsse.ts @@ -135,6 +135,49 @@ const validBundleWithNoTLogEntries = { }, }; +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 @@ -579,6 +622,7 @@ export default { withSigningCert: validBundleWithSigningCert, withPublicKey: validBundleWithPublicKey, withNoTLogEntries: validBundleWithNoTLogEntries, + withDSSETLogEntry: validBundleWithDSSETLogEntry, }, invalid: { badSignature: invalidBadSignature, diff --git a/packages/client/src/__tests__/tlog/verify/body.test.ts b/packages/client/src/__tests__/tlog/verify/body.test.ts index 7332b358..81db230f 100644 --- a/packages/client/src/__tests__/tlog/verify/body.test.ts +++ b/packages/client/src/__tests__/tlog/verify/body.test.ts @@ -140,4 +140,18 @@ describe('verifyTLogBody', () => { }); }); }); + + describe('when a DSSE Bundle w/ dsse tlog entry is provided', () => { + describe('when everything is valid', () => { + const bundle = sigstore.bundleFromJSON( + bundles.dsse.valid.withDSSETLogEntry + ); + const tlogEntry = bundle.verificationMaterial + ?.tlogEntries[0] as sigstore.VerifiableTransparencyLogEntry; + + it('returns true', () => { + expect(verifyTLogBody(tlogEntry, bundle.content)).toBe(true); + }); + }); + }); });