Merge pull request #929 from crazy-max/buildx-verify
buildx(install): use sigstore module to verify signature
This commit is contained in:
@@ -29,7 +29,12 @@ maybe('download', () => {
|
|||||||
const install = new Install({
|
const install = new Install({
|
||||||
standalone: true
|
standalone: true
|
||||||
});
|
});
|
||||||
const toolPath = await install.download(version);
|
const toolPath = await install.download({
|
||||||
|
version: version,
|
||||||
|
verifySignature: true,
|
||||||
|
ghaNoCache: true,
|
||||||
|
disableHtc: true
|
||||||
|
});
|
||||||
if (!fs.existsSync(toolPath)) {
|
if (!fs.existsSync(toolPath)) {
|
||||||
throw new Error('toolPath does not exist');
|
throw new Error('toolPath does not exist');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ describe('download', () => {
|
|||||||
])(
|
])(
|
||||||
'acquires %p of buildx (standalone: %p)', async (version, standalone) => {
|
'acquires %p of buildx (standalone: %p)', async (version, standalone) => {
|
||||||
const install = new Install({standalone: standalone});
|
const install = new Install({standalone: standalone});
|
||||||
const toolPath = await install.download(version);
|
const toolPath = await install.download({version});
|
||||||
expect(fs.existsSync(toolPath)).toBe(true);
|
expect(fs.existsSync(toolPath)).toBe(true);
|
||||||
let buildxBin: string;
|
let buildxBin: string;
|
||||||
if (standalone) {
|
if (standalone) {
|
||||||
@@ -58,7 +58,7 @@ describe('download', () => {
|
|||||||
])(
|
])(
|
||||||
'acquires %p of buildx with cache', async (version) => {
|
'acquires %p of buildx with cache', async (version) => {
|
||||||
const install = new Install({standalone: false});
|
const install = new Install({standalone: false});
|
||||||
const toolPath = await install.download(version);
|
const toolPath = await install.download({version});
|
||||||
expect(fs.existsSync(toolPath)).toBe(true);
|
expect(fs.existsSync(toolPath)).toBe(true);
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ describe('download', () => {
|
|||||||
])(
|
])(
|
||||||
'acquires %p of buildx without cache', async (version) => {
|
'acquires %p of buildx without cache', async (version) => {
|
||||||
const install = new Install({standalone: false});
|
const install = new Install({standalone: false});
|
||||||
const toolPath = await install.download(version, true);
|
const toolPath = await install.download({version: version, ghaNoCache: true});
|
||||||
expect(fs.existsSync(toolPath)).toBe(true);
|
expect(fs.existsSync(toolPath)).toBe(true);
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ describe('download', () => {
|
|||||||
mockPlatform(os as NodeJS.Platform);
|
mockPlatform(os as NodeJS.Platform);
|
||||||
mockArch(arch);
|
mockArch(arch);
|
||||||
const install = new Install();
|
const install = new Install();
|
||||||
const buildxBin = await install.download('latest');
|
const buildxBin = await install.download({version: 'latest'});
|
||||||
expect(fs.existsSync(buildxBin)).toBe(true);
|
expect(fs.existsSync(buildxBin)).toBe(true);
|
||||||
}, 100000);
|
}, 100000);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import fs from 'fs';
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
|
import * as httpm from '@actions/http-client';
|
||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
@@ -29,23 +30,36 @@ import {Exec} from '../exec.js';
|
|||||||
import {Docker} from '../docker/docker.js';
|
import {Docker} from '../docker/docker.js';
|
||||||
import {Git} from '../git.js';
|
import {Git} from '../git.js';
|
||||||
import {GitHub} from '../github.js';
|
import {GitHub} from '../github.js';
|
||||||
|
import {Sigstore} from '../sigstore/sigstore.js';
|
||||||
import {Util} from '../util.js';
|
import {Util} from '../util.js';
|
||||||
|
|
||||||
import {DownloadVersion} from '../types/buildx/buildx.js';
|
import {DownloadVersion} from '../types/buildx/buildx.js';
|
||||||
import {GitHubRelease} from '../types/github.js';
|
import {GitHubRelease} from '../types/github.js';
|
||||||
|
import {SEARCH_URL} from '../types/sigstore/sigstore.js';
|
||||||
|
|
||||||
|
export interface DownloadOpts {
|
||||||
|
version: string;
|
||||||
|
ghaNoCache?: boolean;
|
||||||
|
disableHtc?: boolean;
|
||||||
|
skipState?: boolean;
|
||||||
|
verifySignature?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface InstallOpts {
|
export interface InstallOpts {
|
||||||
standalone?: boolean;
|
standalone?: boolean;
|
||||||
githubToken?: string;
|
githubToken?: string;
|
||||||
|
sigstore?: Sigstore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Install {
|
export class Install {
|
||||||
private readonly standalone: boolean | undefined;
|
private readonly standalone: boolean | undefined;
|
||||||
private readonly githubToken: string | undefined;
|
private readonly githubToken: string | undefined;
|
||||||
|
private readonly sigstore: Sigstore;
|
||||||
|
|
||||||
constructor(opts?: InstallOpts) {
|
constructor(opts?: InstallOpts) {
|
||||||
this.standalone = opts?.standalone;
|
this.standalone = opts?.standalone;
|
||||||
this.githubToken = opts?.githubToken || process.env.GITHUB_TOKEN;
|
this.githubToken = opts?.githubToken || process.env.GITHUB_TOKEN;
|
||||||
|
this.sigstore = opts?.sigstore || new Sigstore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -54,8 +68,8 @@ export class Install {
|
|||||||
* @param ghaNoCache: disable binary caching in GitHub Actions cache backend
|
* @param ghaNoCache: disable binary caching in GitHub Actions cache backend
|
||||||
* @returns path to the buildx binary
|
* @returns path to the buildx binary
|
||||||
*/
|
*/
|
||||||
public async download(v: string, ghaNoCache?: boolean): Promise<string> {
|
public async download(opts: DownloadOpts): Promise<string> {
|
||||||
const version: DownloadVersion = await Install.getDownloadVersion(v);
|
const version: DownloadVersion = await Install.getDownloadVersion(opts.version);
|
||||||
core.debug(`Install.download version: ${version.version}`);
|
core.debug(`Install.download version: ${version.version}`);
|
||||||
|
|
||||||
const release: GitHubRelease = await Install.getRelease(version, this.githubToken);
|
const release: GitHubRelease = await Install.getRelease(version, this.githubToken);
|
||||||
@@ -74,11 +88,11 @@ export class Install {
|
|||||||
htcVersion: vspec,
|
htcVersion: vspec,
|
||||||
baseCacheDir: path.join(Buildx.configDir, '.bin'),
|
baseCacheDir: path.join(Buildx.configDir, '.bin'),
|
||||||
cacheFile: os.platform() == 'win32' ? 'docker-buildx.exe' : 'docker-buildx',
|
cacheFile: os.platform() == 'win32' ? 'docker-buildx.exe' : 'docker-buildx',
|
||||||
ghaNoCache: ghaNoCache
|
ghaNoCache: opts.ghaNoCache
|
||||||
});
|
});
|
||||||
|
|
||||||
const cacheFoundPath = await installCache.find();
|
const cacheFoundPath = await installCache.find();
|
||||||
if (cacheFoundPath) {
|
if (!opts.disableHtc && cacheFoundPath) {
|
||||||
core.info(`Buildx binary found in ${cacheFoundPath}`);
|
core.info(`Buildx binary found in ${cacheFoundPath}`);
|
||||||
return cacheFoundPath;
|
return cacheFoundPath;
|
||||||
}
|
}
|
||||||
@@ -89,7 +103,11 @@ export class Install {
|
|||||||
const htcDownloadPath = await tc.downloadTool(downloadURL, undefined, this.githubToken);
|
const htcDownloadPath = await tc.downloadTool(downloadURL, undefined, this.githubToken);
|
||||||
core.debug(`Install.download htcDownloadPath: ${htcDownloadPath}`);
|
core.debug(`Install.download htcDownloadPath: ${htcDownloadPath}`);
|
||||||
|
|
||||||
const cacheSavePath = await installCache.save(htcDownloadPath);
|
if (opts.verifySignature && semver.satisfies(vspec, '>=0.31.0-0', {includePrerelease: true})) {
|
||||||
|
await this.verifySignature(htcDownloadPath, downloadURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheSavePath = await installCache.save(htcDownloadPath, opts.skipState);
|
||||||
core.info(`Cached to ${cacheSavePath}`);
|
core.info(`Cached to ${cacheSavePath}`);
|
||||||
return cacheSavePath;
|
return cacheSavePath;
|
||||||
}
|
}
|
||||||
@@ -213,6 +231,31 @@ export class Install {
|
|||||||
return standalone;
|
return standalone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async verifySignature(binPath: string, downloadURL: string): Promise<void> {
|
||||||
|
const bundleURL = `${downloadURL.replace(/\.exe$/, '')}.sigstore.json`;
|
||||||
|
core.info(`Downloading keyless verification bundle at ${bundleURL}`);
|
||||||
|
|
||||||
|
let bundlePath: string;
|
||||||
|
try {
|
||||||
|
bundlePath = await tc.downloadTool(bundleURL, undefined, this.githubToken);
|
||||||
|
core.debug(`Install.verifySignature bundlePath: ${bundlePath}`);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message && e.message.statusCode === httpm.HttpCodes.NotFound) {
|
||||||
|
core.info(`No signature bundle found at ${bundleURL}, skipping verification`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyResult = await this.sigstore.verifyArtifact(binPath, bundlePath, {
|
||||||
|
// TODO: add githubWorkflowRepository , runnerEnvironment and sourceRepositoryURI extensions when supported by sigstore module
|
||||||
|
subjectAlternativeName: /^https:\/\/github\.com\/docker\/(github-builder-experimental|github-builder)\/\.github\/workflows\/bake\.yml.*$/,
|
||||||
|
issuer: 'https://token.actions.githubusercontent.com'
|
||||||
|
});
|
||||||
|
|
||||||
|
core.info(`Buildx binary signature verified! ${verifyResult.tlogID ? `${SEARCH_URL}?logIndex=${verifyResult.tlogID}` : ''}`);
|
||||||
|
}
|
||||||
|
|
||||||
private filename(version: string): string {
|
private filename(version: string): string {
|
||||||
let arch: string;
|
let arch: string;
|
||||||
switch (os.arch()) {
|
switch (os.arch()) {
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ import os from 'os';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
import {bundleFromJSON, SerializedBundle} from '@sigstore/bundle';
|
|
||||||
import * as tuf from '@sigstore/tuf';
|
|
||||||
import {toSignedEntity, toTrustMaterial, Verifier} from '@sigstore/verify';
|
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
|
|
||||||
@@ -31,11 +28,13 @@ import {Context} from '../context.js';
|
|||||||
import {Exec} from '../exec.js';
|
import {Exec} from '../exec.js';
|
||||||
import {Git} from '../git.js';
|
import {Git} from '../git.js';
|
||||||
import {GitHub} from '../github.js';
|
import {GitHub} from '../github.js';
|
||||||
|
import {Sigstore} from '../sigstore/sigstore.js';
|
||||||
import {Util} from '../util.js';
|
import {Util} from '../util.js';
|
||||||
|
|
||||||
import {DownloadVersion} from '../types/cosign/cosign.js';
|
import {DownloadVersion} from '../types/cosign/cosign.js';
|
||||||
import {GitHubRelease} from '../types/github.js';
|
import {GitHubRelease} from '../types/github.js';
|
||||||
import {dockerfileContent} from './dockerfile.js';
|
import {dockerfileContent} from './dockerfile.js';
|
||||||
|
import {SEARCH_URL} from '../types/sigstore/sigstore.js';
|
||||||
|
|
||||||
export interface DownloadOpts {
|
export interface DownloadOpts {
|
||||||
version: string;
|
version: string;
|
||||||
@@ -47,15 +46,18 @@ export interface DownloadOpts {
|
|||||||
export interface InstallOpts {
|
export interface InstallOpts {
|
||||||
githubToken?: string;
|
githubToken?: string;
|
||||||
buildx?: Buildx;
|
buildx?: Buildx;
|
||||||
|
sigstore?: Sigstore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Install {
|
export class Install {
|
||||||
private readonly githubToken: string | undefined;
|
private readonly githubToken: string | undefined;
|
||||||
private readonly buildx: Buildx;
|
private readonly buildx: Buildx;
|
||||||
|
private readonly sigstore: Sigstore;
|
||||||
|
|
||||||
constructor(opts?: InstallOpts) {
|
constructor(opts?: InstallOpts) {
|
||||||
this.githubToken = opts?.githubToken || process.env.GITHUB_TOKEN;
|
this.githubToken = opts?.githubToken || process.env.GITHUB_TOKEN;
|
||||||
this.buildx = opts?.buildx || new Buildx();
|
this.buildx = opts?.buildx || new Buildx();
|
||||||
|
this.sigstore = opts?.sigstore || new Sigstore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async download(opts: DownloadOpts): Promise<string> {
|
public async download(opts: DownloadOpts): Promise<string> {
|
||||||
@@ -196,27 +198,12 @@ export class Install {
|
|||||||
const bundlePath = await tc.downloadTool(bundleURL, undefined, this.githubToken);
|
const bundlePath = await tc.downloadTool(bundleURL, undefined, this.githubToken);
|
||||||
core.debug(`Install.verifySignature bundlePath: ${bundlePath}`);
|
core.debug(`Install.verifySignature bundlePath: ${bundlePath}`);
|
||||||
|
|
||||||
core.info(`Verifying keyless verification bundle signature`);
|
const verifyResult = await this.sigstore.verifyArtifact(cosignBinPath, bundlePath, {
|
||||||
const parsedBundle = JSON.parse(fs.readFileSync(bundlePath, 'utf-8')) as SerializedBundle;
|
subjectAlternativeName: 'keyless@projectsigstore.iam.gserviceaccount.com',
|
||||||
const bundle = bundleFromJSON(parsedBundle);
|
issuer: 'https://accounts.google.com'
|
||||||
|
});
|
||||||
|
|
||||||
core.info(`Fetching Sigstore TUF trusted root metadata`);
|
core.info(`Cosign binary signature verified! ${verifyResult.tlogID ? `${SEARCH_URL}?logIndex=${verifyResult.tlogID}` : ''}`);
|
||||||
const trustedRoot = await tuf.getTrustedRoot();
|
|
||||||
const trustMaterial = toTrustMaterial(trustedRoot);
|
|
||||||
|
|
||||||
try {
|
|
||||||
core.info(`Verifying cosign binary signature`);
|
|
||||||
const signedEntity = toSignedEntity(bundle, fs.readFileSync(cosignBinPath));
|
|
||||||
const verifier = new Verifier(trustMaterial);
|
|
||||||
const signer = verifier.verify(signedEntity, {
|
|
||||||
subjectAlternativeName: 'keyless@projectsigstore.iam.gserviceaccount.com',
|
|
||||||
extensions: {issuer: 'https://accounts.google.com'}
|
|
||||||
});
|
|
||||||
core.debug(`Install.verifySignature signer: ${JSON.stringify(signer)}`);
|
|
||||||
core.info(`Cosign binary signature verified!`);
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Failed to verify cosign binary signature: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private filename(): string {
|
private filename(): string {
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import {bundleFromJSON, bundleToJSON} from '@sigstore/bundle';
|
import {bundleFromJSON, bundleToJSON, SerializedBundle} from '@sigstore/bundle';
|
||||||
import {Artifact, Bundle, CIContextProvider, DSSEBundleBuilder, FulcioSigner, RekorWitness, TSAWitness, Witness} from '@sigstore/sign';
|
import {Artifact, Bundle, CIContextProvider, DSSEBundleBuilder, FulcioSigner, RekorWitness, TSAWitness, Witness} from '@sigstore/sign';
|
||||||
|
import * as tuf from '@sigstore/tuf';
|
||||||
|
import {toSignedEntity, toTrustMaterial, Verifier} from '@sigstore/verify';
|
||||||
|
|
||||||
import {Context} from '../context.js';
|
import {Context} from '../context.js';
|
||||||
import {Cosign} from '../cosign/cosign.js';
|
import {Cosign} from '../cosign/cosign.js';
|
||||||
@@ -40,6 +42,8 @@ import {
|
|||||||
SignProvenanceBlobsOpts,
|
SignProvenanceBlobsOpts,
|
||||||
SignProvenanceBlobsResult,
|
SignProvenanceBlobsResult,
|
||||||
TSASERVER_URL,
|
TSASERVER_URL,
|
||||||
|
VerifyArtifactOpts,
|
||||||
|
VerifyArtifactResult,
|
||||||
VerifySignedArtifactsOpts,
|
VerifySignedArtifactsOpts,
|
||||||
VerifySignedArtifactsResult,
|
VerifySignedArtifactsResult,
|
||||||
VerifySignedManifestsOpts,
|
VerifySignedManifestsOpts,
|
||||||
@@ -361,6 +365,51 @@ export class Sigstore {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async verifyArtifact(artifactPath: string, bundlePath: string, opts?: VerifyArtifactOpts): Promise<VerifyArtifactResult> {
|
||||||
|
core.info(`Verifying keyless verification bundle signature`);
|
||||||
|
const parsedBundle = JSON.parse(fs.readFileSync(bundlePath, 'utf-8')) as SerializedBundle;
|
||||||
|
const bundle = bundleFromJSON(parsedBundle);
|
||||||
|
|
||||||
|
core.info(`Fetching Sigstore TUF trusted root metadata`);
|
||||||
|
const trustedRoot = await tuf.getTrustedRoot();
|
||||||
|
const trustMaterial = toTrustMaterial(trustedRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
core.info(`Verifying artifact signature`);
|
||||||
|
const signedEntity = toSignedEntity(bundle, fs.readFileSync(artifactPath));
|
||||||
|
const signingCert = Sigstore.parseCertificate(bundle);
|
||||||
|
|
||||||
|
// collect transparency log ID if available
|
||||||
|
const tlogEntries = bundle.verificationMaterial.tlogEntries;
|
||||||
|
const tlogID = tlogEntries.length > 0 ? tlogEntries[0].logIndex : undefined;
|
||||||
|
|
||||||
|
// TODO: remove when subjectAlternativeName check with regex is supported: https://github.com/sigstore/sigstore-js/pull/1556
|
||||||
|
if (opts?.subjectAlternativeName && opts?.subjectAlternativeName instanceof RegExp) {
|
||||||
|
const subjectAltName = signingCert.subjectAltName?.replace(/^uri:/i, '');
|
||||||
|
if (!subjectAltName) {
|
||||||
|
throw new Error('Signing certificate does not contain subjectAltName');
|
||||||
|
} else if (!subjectAltName.match(opts.subjectAlternativeName)) {
|
||||||
|
throw new Error(`Signing certificate subjectAlternativeName "${subjectAltName}" does not match expected pattern`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifier = new Verifier(trustMaterial);
|
||||||
|
const signer = verifier.verify(signedEntity, {
|
||||||
|
subjectAlternativeName: opts?.subjectAlternativeName && typeof opts.subjectAlternativeName === 'string' ? opts.subjectAlternativeName : undefined,
|
||||||
|
extensions: opts?.issuer ? {issuer: opts.issuer} : undefined
|
||||||
|
});
|
||||||
|
core.debug(`Sigstore.verifyArtifact signer: ${JSON.stringify(signer)}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
payload: parsedBundle,
|
||||||
|
certificate: signingCert.toString(),
|
||||||
|
tlogID: tlogID
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Failed to verify artifact signature: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private signingEndpoints(noTransparencyLog?: boolean): Endpoints {
|
private signingEndpoints(noTransparencyLog?: boolean): Endpoints {
|
||||||
noTransparencyLog = Sigstore.noTransparencyLog(noTransparencyLog);
|
noTransparencyLog = Sigstore.noTransparencyLog(noTransparencyLog);
|
||||||
core.info(`Upload to transparency log: ${noTransparencyLog ? 'disabled' : 'enabled'}`);
|
core.info(`Upload to transparency log: ${noTransparencyLog ? 'disabled' : 'enabled'}`);
|
||||||
@@ -442,19 +491,7 @@ export class Sigstore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static parseBundle(bundle: Bundle): ParsedBundle {
|
private static parseBundle(bundle: Bundle): ParsedBundle {
|
||||||
let certBytes: Buffer;
|
const signingCert = Sigstore.parseCertificate(bundle);
|
||||||
switch (bundle.verificationMaterial.content.$case) {
|
|
||||||
case 'x509CertificateChain':
|
|
||||||
certBytes = bundle.verificationMaterial.content.x509CertificateChain.certificates[0].rawBytes;
|
|
||||||
break;
|
|
||||||
case 'certificate':
|
|
||||||
certBytes = bundle.verificationMaterial.content.certificate.rawBytes;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error('Bundle must contain an x509 certificate');
|
|
||||||
}
|
|
||||||
|
|
||||||
const signingCert = new X509Certificate(certBytes);
|
|
||||||
|
|
||||||
// collect transparency log ID if available
|
// collect transparency log ID if available
|
||||||
const tlogEntries = bundle.verificationMaterial.tlogEntries;
|
const tlogEntries = bundle.verificationMaterial.tlogEntries;
|
||||||
@@ -466,4 +503,19 @@ export class Sigstore {
|
|||||||
tlogID: tlogID
|
tlogID: tlogID
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static parseCertificate(bundle: Bundle): X509Certificate {
|
||||||
|
let certBytes: Buffer;
|
||||||
|
switch (bundle.verificationMaterial.content.$case) {
|
||||||
|
case 'x509CertificateChain':
|
||||||
|
certBytes = bundle.verificationMaterial.content.x509CertificateChain.certificates[0].rawBytes;
|
||||||
|
break;
|
||||||
|
case 'certificate':
|
||||||
|
certBytes = bundle.verificationMaterial.content.certificate.rawBytes;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Bundle must contain an x509 certificate');
|
||||||
|
}
|
||||||
|
return new X509Certificate(certBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,3 +78,14 @@ export interface VerifySignedArtifactsResult {
|
|||||||
bundlePath: string;
|
bundlePath: string;
|
||||||
cosignArgs: Array<string>;
|
cosignArgs: Array<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VerifyArtifactOpts {
|
||||||
|
subjectAlternativeName: string | RegExp;
|
||||||
|
issuer?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VerifyArtifactResult {
|
||||||
|
payload: SerializedBundle;
|
||||||
|
certificate: string;
|
||||||
|
tlogID?: string;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user