Skip to content

Commit

Permalink
add Rekor inclusion proof to Sigstore bundle (#578)
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
bdehamer committed Jun 30, 2023
1 parent ad5b67a commit f374dd3
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-carpets-help.md
@@ -0,0 +1,5 @@
---
'sigstore': minor
---

Include transparency log inclusion proof in Sigstore bundle
40 changes: 38 additions & 2 deletions packages/client/src/__tests__/sign.test.ts
Expand Up @@ -23,6 +23,8 @@ import { SignatureMaterial, SignerFunc } from '../types/signature';
import { HashAlgorithm } from '../types/sigstore';
import { pem } from '../util';

import type { LogEntry } from '@sigstore/rekor-types';

describe('Signer', () => {
const fulcioBaseURL = 'http://localhost:8001';
const rekorBaseURL = 'http://localhost:8002';
Expand Down Expand Up @@ -140,9 +142,16 @@ describe('Signer', () => {
verification: {
signedEntryTimestamp:
'MEUCIQD6CD7ZNLUipFoxzmSL/L8Ewic4SRkXN77UjfJZ7d/wAAIgatokSuX9Rg0iWxAgSfHMtcsagtDCQalU5IvXdQ+yLEA=',
inclusionProof: {
hashes: ['deadbeef', 'feedface'],
logIndex: 12345,
rootHash: 'fee1dead',
treeSize: 12346,
checkpoint: 'checkpoint',
},
},
},
};
} satisfies LogEntry;

beforeEach(() => {
// Mock Rekor request
Expand Down Expand Up @@ -211,7 +220,34 @@ describe('Signer', () => {
expect(tlog?.logIndex).toEqual(
rekorEntry[uuid].logIndex.toString()
);
expect(tlog?.inclusionProof).toBeFalsy();
expect(tlog?.inclusionProof?.checkpoint?.envelope).toEqual(
rekorEntry[uuid].verification.inclusionProof.checkpoint
);
expect(tlog?.inclusionProof?.hashes).toHaveLength(2);
expect(tlog?.inclusionProof?.hashes[0]).toEqual(
Buffer.from(
rekorEntry[uuid].verification.inclusionProof.hashes[0],
'hex'
)
);
expect(tlog?.inclusionProof?.hashes[1]).toEqual(
Buffer.from(
rekorEntry[uuid].verification.inclusionProof.hashes[1],
'hex'
)
);
expect(tlog?.inclusionProof?.logIndex).toEqual(
rekorEntry[uuid].verification.inclusionProof.logIndex.toString()
);
expect(tlog?.inclusionProof?.rootHash).toEqual(
Buffer.from(
rekorEntry[uuid].verification.inclusionProof.rootHash,
'hex'
)
);
expect(tlog?.inclusionProof?.treeSize).toEqual(
rekorEntry[uuid].verification.inclusionProof.treeSize.toString()
);
expect(tlog?.kindVersion?.kind).toEqual('hashedrekord');
expect(tlog?.kindVersion?.version).toEqual('0.0.1');
});
Expand Down
30 changes: 24 additions & 6 deletions packages/client/src/__tests__/types/sigstore/index.test.ts
Expand Up @@ -145,11 +145,11 @@ describe('bundle', () => {
verification: {
signedEntryTimestamp: Buffer.from('set').toString('base64'),
inclusionProof: {
hashes: [],
logIndex: 0,
rootHash: '',
treeSize: 0,
checkpoint: '',
hashes: ['deadbeef', 'feedface'],
logIndex: 12345,
rootHash: 'fee1dead',
treeSize: 12346,
checkpoint: 'checkpoint',
},
},
} satisfies Entry;
Expand Down Expand Up @@ -199,9 +199,27 @@ describe('bundle', () => {
expect(tlog?.logId?.keyId).toBeTruthy();
expect(tlog?.logId?.keyId.toString('hex')).toEqual(rekorEntry.logID);
expect(tlog?.logIndex).toEqual(rekorEntry.logIndex.toString());
expect(tlog?.inclusionProof).toBeFalsy();
expect(tlog?.kindVersion?.kind).toEqual(entryKind.kind);
expect(tlog?.kindVersion?.version).toEqual(entryKind.apiVersion);
expect(tlog?.inclusionProof?.checkpoint?.envelope).toEqual(
rekorEntry.verification.inclusionProof.checkpoint
);
expect(tlog?.inclusionProof?.hashes).toHaveLength(2);
expect(tlog?.inclusionProof?.hashes[0]).toEqual(
Buffer.from(rekorEntry.verification.inclusionProof.hashes[0], 'hex')
);
expect(tlog?.inclusionProof?.hashes[1]).toEqual(
Buffer.from(rekorEntry.verification.inclusionProof.hashes[1], 'hex')
);
expect(tlog?.inclusionProof?.logIndex).toEqual(
rekorEntry.verification.inclusionProof.logIndex.toString()
);
expect(tlog?.inclusionProof?.rootHash).toEqual(
Buffer.from(rekorEntry.verification.inclusionProof.rootHash, 'hex')
);
expect(tlog?.inclusionProof?.treeSize).toEqual(
rekorEntry.verification.inclusionProof.treeSize.toString()
);

// Timestamp verification data
expect(
Expand Down
10 changes: 6 additions & 4 deletions packages/client/src/external/rekor.ts
Expand Up @@ -19,22 +19,24 @@ import { checkStatus } from './error';

import type {
LogEntry,
ProposedEntry,
ProposedDSSEEntry,
ProposedEntry,
ProposedHashedRekordEntry,
ProposedIntotoEntry,
InclusionProof as RekorInclusionProof,
SearchIndex,
SearchLogQuery,
} from '@sigstore/rekor-types';
import type { FetchOptions } from '../types/fetch';

export type {
ProposedEntry,
SearchIndex,
SearchLogQuery,
ProposedDSSEEntry,
ProposedEntry,
ProposedHashedRekordEntry,
ProposedIntotoEntry,
RekorInclusionProof,
SearchIndex,
SearchLogQuery,
};

// The LogEntry type from @sigstore/rekor-types is a Record type
Expand Down
24 changes: 22 additions & 2 deletions packages/client/src/types/sigstore/index.ts
Expand Up @@ -21,11 +21,16 @@ import { ValidBundle, assertValidBundle } from './validate';
import type {
ArtifactVerificationOptions,
Envelope,
InclusionProof,
TimestampVerificationData,
TransparencyLogEntry,
VerificationMaterial,
} from '@sigstore/protobuf-specs';
import type { Entry, ProposedEntry } from '../../external/rekor';
import type {
Entry,
ProposedEntry,
RekorInclusionProof,
} from '../../external/rekor';
import type { WithRequired } from '../utility';
import type { SerializedBundle } from './serialized';

Expand Down Expand Up @@ -200,6 +205,9 @@ function toTransparencyLogEntry(entry: Entry): TransparencyLogEntry {
const b64SET = entry.verification?.signedEntryTimestamp || '';
const set = Buffer.from(b64SET, 'base64');
const logID = Buffer.from(entry.logID, 'hex');
const proof = entry.verification?.inclusionProof
? toInclusionProof(entry.verification.inclusionProof)
: undefined;

// Parse entry body so we can extract the kind and version.
const bodyJSON = enc.base64Decode(entry.body);
Expand All @@ -218,11 +226,23 @@ function toTransparencyLogEntry(entry: Entry): TransparencyLogEntry {
kind: entryBody.kind,
version: entryBody.apiVersion,
},
inclusionProof: undefined,
inclusionProof: proof,
canonicalizedBody: Buffer.from(entry.body, 'base64'),
};
}

function toInclusionProof(proof: RekorInclusionProof): InclusionProof {
return {
logIndex: proof.logIndex.toString(),
rootHash: Buffer.from(proof.rootHash, 'hex'),
treeSize: proof.treeSize.toString(),
checkpoint: {
envelope: proof.checkpoint,
},
hashes: proof.hashes.map((h) => Buffer.from(h, 'hex')),
};
}

function toVerificationMaterial({
signature,
tlogEntry,
Expand Down

0 comments on commit f374dd3

Please sign in to comment.