Skip to content

Commit f374dd3

Browse files
authoredJun 30, 2023
add Rekor inclusion proof to Sigstore bundle (#578)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
1 parent ad5b67a commit f374dd3

File tree

5 files changed

+95
-14
lines changed

5 files changed

+95
-14
lines changed
 

‎.changeset/serious-carpets-help.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sigstore': minor
3+
---
4+
5+
Include transparency log inclusion proof in Sigstore bundle

‎packages/client/src/__tests__/sign.test.ts

+38-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { SignatureMaterial, SignerFunc } from '../types/signature';
2323
import { HashAlgorithm } from '../types/sigstore';
2424
import { pem } from '../util';
2525

26+
import type { LogEntry } from '@sigstore/rekor-types';
27+
2628
describe('Signer', () => {
2729
const fulcioBaseURL = 'http://localhost:8001';
2830
const rekorBaseURL = 'http://localhost:8002';
@@ -140,9 +142,16 @@ describe('Signer', () => {
140142
verification: {
141143
signedEntryTimestamp:
142144
'MEUCIQD6CD7ZNLUipFoxzmSL/L8Ewic4SRkXN77UjfJZ7d/wAAIgatokSuX9Rg0iWxAgSfHMtcsagtDCQalU5IvXdQ+yLEA=',
145+
inclusionProof: {
146+
hashes: ['deadbeef', 'feedface'],
147+
logIndex: 12345,
148+
rootHash: 'fee1dead',
149+
treeSize: 12346,
150+
checkpoint: 'checkpoint',
151+
},
143152
},
144153
},
145-
};
154+
} satisfies LogEntry;
146155

147156
beforeEach(() => {
148157
// Mock Rekor request
@@ -211,7 +220,34 @@ describe('Signer', () => {
211220
expect(tlog?.logIndex).toEqual(
212221
rekorEntry[uuid].logIndex.toString()
213222
);
214-
expect(tlog?.inclusionProof).toBeFalsy();
223+
expect(tlog?.inclusionProof?.checkpoint?.envelope).toEqual(
224+
rekorEntry[uuid].verification.inclusionProof.checkpoint
225+
);
226+
expect(tlog?.inclusionProof?.hashes).toHaveLength(2);
227+
expect(tlog?.inclusionProof?.hashes[0]).toEqual(
228+
Buffer.from(
229+
rekorEntry[uuid].verification.inclusionProof.hashes[0],
230+
'hex'
231+
)
232+
);
233+
expect(tlog?.inclusionProof?.hashes[1]).toEqual(
234+
Buffer.from(
235+
rekorEntry[uuid].verification.inclusionProof.hashes[1],
236+
'hex'
237+
)
238+
);
239+
expect(tlog?.inclusionProof?.logIndex).toEqual(
240+
rekorEntry[uuid].verification.inclusionProof.logIndex.toString()
241+
);
242+
expect(tlog?.inclusionProof?.rootHash).toEqual(
243+
Buffer.from(
244+
rekorEntry[uuid].verification.inclusionProof.rootHash,
245+
'hex'
246+
)
247+
);
248+
expect(tlog?.inclusionProof?.treeSize).toEqual(
249+
rekorEntry[uuid].verification.inclusionProof.treeSize.toString()
250+
);
215251
expect(tlog?.kindVersion?.kind).toEqual('hashedrekord');
216252
expect(tlog?.kindVersion?.version).toEqual('0.0.1');
217253
});

‎packages/client/src/__tests__/types/sigstore/index.test.ts

+24-6
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ describe('bundle', () => {
145145
verification: {
146146
signedEntryTimestamp: Buffer.from('set').toString('base64'),
147147
inclusionProof: {
148-
hashes: [],
149-
logIndex: 0,
150-
rootHash: '',
151-
treeSize: 0,
152-
checkpoint: '',
148+
hashes: ['deadbeef', 'feedface'],
149+
logIndex: 12345,
150+
rootHash: 'fee1dead',
151+
treeSize: 12346,
152+
checkpoint: 'checkpoint',
153153
},
154154
},
155155
} satisfies Entry;
@@ -199,9 +199,27 @@ describe('bundle', () => {
199199
expect(tlog?.logId?.keyId).toBeTruthy();
200200
expect(tlog?.logId?.keyId.toString('hex')).toEqual(rekorEntry.logID);
201201
expect(tlog?.logIndex).toEqual(rekorEntry.logIndex.toString());
202-
expect(tlog?.inclusionProof).toBeFalsy();
203202
expect(tlog?.kindVersion?.kind).toEqual(entryKind.kind);
204203
expect(tlog?.kindVersion?.version).toEqual(entryKind.apiVersion);
204+
expect(tlog?.inclusionProof?.checkpoint?.envelope).toEqual(
205+
rekorEntry.verification.inclusionProof.checkpoint
206+
);
207+
expect(tlog?.inclusionProof?.hashes).toHaveLength(2);
208+
expect(tlog?.inclusionProof?.hashes[0]).toEqual(
209+
Buffer.from(rekorEntry.verification.inclusionProof.hashes[0], 'hex')
210+
);
211+
expect(tlog?.inclusionProof?.hashes[1]).toEqual(
212+
Buffer.from(rekorEntry.verification.inclusionProof.hashes[1], 'hex')
213+
);
214+
expect(tlog?.inclusionProof?.logIndex).toEqual(
215+
rekorEntry.verification.inclusionProof.logIndex.toString()
216+
);
217+
expect(tlog?.inclusionProof?.rootHash).toEqual(
218+
Buffer.from(rekorEntry.verification.inclusionProof.rootHash, 'hex')
219+
);
220+
expect(tlog?.inclusionProof?.treeSize).toEqual(
221+
rekorEntry.verification.inclusionProof.treeSize.toString()
222+
);
205223

206224
// Timestamp verification data
207225
expect(

‎packages/client/src/external/rekor.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,24 @@ import { checkStatus } from './error';
1919

2020
import type {
2121
LogEntry,
22-
ProposedEntry,
2322
ProposedDSSEEntry,
23+
ProposedEntry,
2424
ProposedHashedRekordEntry,
2525
ProposedIntotoEntry,
26+
InclusionProof as RekorInclusionProof,
2627
SearchIndex,
2728
SearchLogQuery,
2829
} from '@sigstore/rekor-types';
2930
import type { FetchOptions } from '../types/fetch';
3031

3132
export type {
32-
ProposedEntry,
33-
SearchIndex,
34-
SearchLogQuery,
3533
ProposedDSSEEntry,
34+
ProposedEntry,
3635
ProposedHashedRekordEntry,
3736
ProposedIntotoEntry,
37+
RekorInclusionProof,
38+
SearchIndex,
39+
SearchLogQuery,
3840
};
3941

4042
// The LogEntry type from @sigstore/rekor-types is a Record type

‎packages/client/src/types/sigstore/index.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ import { ValidBundle, assertValidBundle } from './validate';
2121
import type {
2222
ArtifactVerificationOptions,
2323
Envelope,
24+
InclusionProof,
2425
TimestampVerificationData,
2526
TransparencyLogEntry,
2627
VerificationMaterial,
2728
} from '@sigstore/protobuf-specs';
28-
import type { Entry, ProposedEntry } from '../../external/rekor';
29+
import type {
30+
Entry,
31+
ProposedEntry,
32+
RekorInclusionProof,
33+
} from '../../external/rekor';
2934
import type { WithRequired } from '../utility';
3035
import type { SerializedBundle } from './serialized';
3136

@@ -200,6 +205,9 @@ function toTransparencyLogEntry(entry: Entry): TransparencyLogEntry {
200205
const b64SET = entry.verification?.signedEntryTimestamp || '';
201206
const set = Buffer.from(b64SET, 'base64');
202207
const logID = Buffer.from(entry.logID, 'hex');
208+
const proof = entry.verification?.inclusionProof
209+
? toInclusionProof(entry.verification.inclusionProof)
210+
: undefined;
203211

204212
// Parse entry body so we can extract the kind and version.
205213
const bodyJSON = enc.base64Decode(entry.body);
@@ -218,11 +226,23 @@ function toTransparencyLogEntry(entry: Entry): TransparencyLogEntry {
218226
kind: entryBody.kind,
219227
version: entryBody.apiVersion,
220228
},
221-
inclusionProof: undefined,
229+
inclusionProof: proof,
222230
canonicalizedBody: Buffer.from(entry.body, 'base64'),
223231
};
224232
}
225233

234+
function toInclusionProof(proof: RekorInclusionProof): InclusionProof {
235+
return {
236+
logIndex: proof.logIndex.toString(),
237+
rootHash: Buffer.from(proof.rootHash, 'hex'),
238+
treeSize: proof.treeSize.toString(),
239+
checkpoint: {
240+
envelope: proof.checkpoint,
241+
},
242+
hashes: proof.hashes.map((h) => Buffer.from(h, 'hex')),
243+
};
244+
}
245+
226246
function toVerificationMaterial({
227247
signature,
228248
tlogEntry,

0 commit comments

Comments
 (0)
Please sign in to comment.