Skip to content

Commit

Permalink
implement all flags for conformance verify cmd
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
bdehamer committed Dec 4, 2024
1 parent cd51310 commit b5841a6
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 89 deletions.
5 changes: 5 additions & 0 deletions .changeset/late-hounds-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sigstore/conformance': minor
---

Implement `--trusted-root` flag on `verify` command.
1 change: 0 additions & 1 deletion .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ jobs:
- uses: sigstore/sigstore-conformance@6bd1c54e236c9517da56f7344ad16cc00439fe19 # v0.0.13
with:
entrypoint: ${{ github.workspace }}/packages/conformance/bin/run
xfail: "test_verify_with_trust_root"

conformance-staging:
name: Conformance Test (Staging)
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/conformance/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"dependencies": {
"@oclif/core": "^4",
"@sigstore/bundle": "^3.0.0",
"@sigstore/core": "^2.0.0",
"@sigstore/protobuf-specs": "^0.3.2",
"@sigstore/tuf": "^3.0.0",
"@sigstore/verify": "^2.0.0",
"elliptic": "^6.6.1",
"sigstore": "^3.0.0"
Expand Down
40 changes: 2 additions & 38 deletions packages/conformance/src/commands/verify-bundle.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import { Args, Command, Flags } from '@oclif/core';
import { Bundle, bundleFromJSON, MessageSignature } from '@sigstore/bundle';
import { TrustedRoot } from '@sigstore/protobuf-specs';
import * as tuf from '@sigstore/tuf';
import {
SignedEntity,
toSignedEntity,
toTrustMaterial,
TrustMaterial,
Verifier,
} from '@sigstore/verify';
import { SignedEntity, toSignedEntity, Verifier } from '@sigstore/verify';
import { ec as EC } from 'elliptic';
import { existsSync } from 'fs';
import fs from 'fs/promises';
import crypto from 'node:crypto';
import os from 'os';
import path from 'path';
import { TUF_STAGING_ROOT, TUF_STAGING_URL } from '../staging';
import { trustMaterialFromPath, trustMaterialFromTUF } from '../trust';

const DIGEST_PREFIX = 'sha256:';

Expand Down Expand Up @@ -78,32 +68,6 @@ export default class VerifyBundle extends Command {
}
}

// Initialize TrustMaterial from TUF
async function trustMaterialFromTUF(staging: boolean): Promise<TrustMaterial> {
const opts: tuf.TUFOptions = {};

if (staging) {
// Write the initial root.json to a temporary directory
const tmpPath = await fs.mkdtemp(path.join(os.tmpdir(), 'sigstore-'));
const rootPath = path.join(tmpPath, 'root.json');
await fs.writeFile(rootPath, Buffer.from(TUF_STAGING_ROOT, 'base64'));

opts.mirrorURL = TUF_STAGING_URL;
opts.rootPath = rootPath;
}

const trustedRoot = await tuf.getTrustedRoot(opts);
return toTrustMaterial(trustedRoot);
}

// Initialize TrustMaterial from a file
async function trustMaterialFromPath(path: string): Promise<TrustMaterial> {
const trustedRoot = await fs
.readFile(path)
.then((data) => JSON.parse(data.toString()));
return toTrustMaterial(TrustedRoot.fromJSON(trustedRoot));
}

// Initialize SignedEntity with the artifact to verify
async function signedEntityFromFile(
bundle: Bundle,
Expand Down
84 changes: 35 additions & 49 deletions packages/conformance/src/commands/verify.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Args, Command, Flags } from '@oclif/core';
import crypto, { BinaryLike } from 'crypto';
import { Bundle, bundleFromJSON } from '@sigstore/bundle';
import { crypto, pem } from '@sigstore/core';
import { toSignedEntity, Verifier } from '@sigstore/verify';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import * as sigstore from 'sigstore';
import { TUF_STAGING_ROOT, TUF_STAGING_URL } from '../staging';
import { trustMaterialFromPath, trustMaterialFromTUF } from '../trust';

export default class Verify extends Command {
static override flags = {
Expand All @@ -25,6 +24,10 @@ export default class Verify extends Command {
description: 'the expected OIDC issuer for the signing certificate',
required: true,
}),
'trusted-root': Flags.string({
description: 'path to trusted root',
required: false,
}),
staging: Flags.boolean({
description: 'whether to use the staging environment',
default: false,
Expand All @@ -42,6 +45,18 @@ export default class Verify extends Command {
public async run(): Promise<void> {
const { args, flags } = await this.parse(Verify);

const trustedRootPath = flags['trusted-root'];
const trustMaterial = trustedRootPath
? await trustMaterialFromPath(trustedRootPath)
: await trustMaterialFromTUF(flags['staging']);

const verifier = new Verifier(trustMaterial, {
tlogThreshold: 0,
tsaThreshold: 0,
});

// Read the artifact, certificate, and signature and assemble them into a
// Sigstore bundle
const artifact = await fs.readFile(args.file);
const certificate = await fs
.readFile(flags.certificate)
Expand All @@ -51,43 +66,35 @@ export default class Verify extends Command {
.then((data) => data.toString());

const bundle = toBundle(artifact, certificate, signature);
const signedEntity = toSignedEntity(bundle, artifact);

const options: Parameters<typeof sigstore.verify>[2] = {
certificateIdentityURI: flags['certificate-identity'],
certificateIssuer: flags['certificate-oidc-issuer'],
tlogThreshold: 0,
const policy = {
subjectAlternativeName: flags['certificate-identity'],
extensions: { issuer: flags['certificate-oidc-issuer'] },
};

if (flags['staging']) {
// Write the initial root.json to a temporary directory
const tmpPath = await fs.mkdtemp(path.join(os.tmpdir(), 'sigstore-'));
const rootPath = path.join(tmpPath, 'root.json');
await fs.writeFile(rootPath, Buffer.from(TUF_STAGING_ROOT, 'base64'));

options.tufMirrorURL = TUF_STAGING_URL;
options.tufRootPath = rootPath;
}

sigstore.verify(bundle, artifact, options);
verifier.verify(signedEntity, policy);
}
}

// Construct a Sigstore bundle from the loose artifact, certificate, and
// signature
function toBundle(
artifact: Buffer,
certificate: string,
signature: string
): sigstore.Bundle {
const artifactDigest = hash(artifact);
const certBytes = toDER(certificate);
): Bundle {
const artifactDigest = crypto.digest('sha256', artifact);
const certBytes = pem.toDER(certificate);

return {
mediaType: 'application/vnd.dev.sigstore.bundle+json;version=0.1',
return bundleFromJSON({
mediaType: 'application/vnd.dev.sigstore.bundle.v0.3+json',
verificationMaterial: {
x509CertificateChain: {
certificates: [{ rawBytes: certBytes.toString('base64') }],
certificate: {
rawBytes: certBytes.toString('base64'),
},
certificate: undefined,
publicKey: undefined,
x509CertificateChain: undefined,
tlogEntries: [],
timestampVerificationData: undefined,
},
Expand All @@ -99,26 +106,5 @@ function toBundle(
},
signature,
},
};
}

function toDER(certificate: string): Buffer {
let der = '';

certificate.split('\n').forEach((line) => {
if (
line.match(/-----BEGIN (.*)-----/) ||
line.match(/-----END (.*)-----/)
) {
return;
}

der += line;
});

return Buffer.from(der, 'base64');
}

export function hash(data: BinaryLike): Buffer {
return crypto.createHash('sha256').update(data).digest();
}
37 changes: 37 additions & 0 deletions packages/conformance/src/trust.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TrustedRoot } from '@sigstore/protobuf-specs';
import * as tuf from '@sigstore/tuf';
import { toTrustMaterial, TrustMaterial } from '@sigstore/verify';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { TUF_STAGING_ROOT, TUF_STAGING_URL } from './staging';

// Initialize TrustMaterial from TUF
export async function trustMaterialFromTUF(
staging: boolean
): Promise<TrustMaterial> {
const opts: tuf.TUFOptions = {};

if (staging) {
// Write the initial root.json to a temporary directory
const tmpPath = await fs.mkdtemp(path.join(os.tmpdir(), 'sigstore-'));
const rootPath = path.join(tmpPath, 'root.json');
await fs.writeFile(rootPath, Buffer.from(TUF_STAGING_ROOT, 'base64'));

opts.mirrorURL = TUF_STAGING_URL;
opts.rootPath = rootPath;
}

const trustedRoot = await tuf.getTrustedRoot(opts);
return toTrustMaterial(trustedRoot);
}

// Initialize TrustMaterial from a file
export async function trustMaterialFromPath(
path: string
): Promise<TrustMaterial> {
const trustedRoot = await fs
.readFile(path)
.then((data) => JSON.parse(data.toString()));
return toTrustMaterial(TrustedRoot.fromJSON(trustedRoot));
}
6 changes: 5 additions & 1 deletion packages/conformance/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
},
"exclude": ["./dist"],
"references": [
{ "path": "../client" }
{ "path": "../bundle" },
{ "path": "../client" },
{ "path": "../core" },
{ "path": "../tuf" },
{ "path": "../verify" }
]
}

0 comments on commit b5841a6

Please sign in to comment.