Add public signing key

This commit is contained in:
Jill Regan
2026-05-21 15:14:34 -04:00
parent cc789f0882
commit 7b7cb42941
6 changed files with 329 additions and 44 deletions
+178 -3
View File
@@ -35369,8 +35369,60 @@ class CliInstaller {
}
}
;// CONCATENATED MODULE: ./src/op-cli-installer/github-action/cli-installer/linux-signature.ts
const execFileAsync = (0,external_util_.promisify)(external_child_process_.execFile);
// 1Password's code-signing GPG key fingerprint. See
// https://www.1password.dev/cli/verify.
const ONEPASSWORD_GPG_KEY_FINGERPRINT = "3FEF9748469ADBE15DA7CA80AC2D62742012EA22";
// Bundled 1Password code-signing public key `linux-signing-key.asc` in
// this directory. Bundled to avoid a runtime keyserver/URL dependency.
// Source: https://downloads.1password.com/linux/keys/1password.asc
const ONEPASSWORD_GPG_PUBLIC_KEY_PATH = __nccwpck_require__.ab + "linux-signing-key.asc";
const defaultGpgRunner = async (args) => {
const { stdout } = await execFileAsync("gpg", args);
return stdout;
};
// Throws unless the binary at opPath carries a valid GPG signature (at
// sigPath) from the pinned 1Password key. The key is bundled with the action
const verifyLinuxSignature = async (opPath, sigPath, runGpg = defaultGpgRunner) => {
const gpgHome = external_fs_.mkdtempSync(external_path_.join(external_os_.tmpdir(), "op-verify-"));
try {
const baseArgs = ["--homedir", gpgHome, "--batch", "--no-tty"];
// Import the bundled key into the temp keyring.
await runGpg([...baseArgs, "--import", __nccwpck_require__.ab + "linux-signing-key.asc"]);
// Confirm we imported the pinned key.
const keyringListing = await runGpg([
...baseArgs,
"--list-keys",
"--with-colons",
]);
if (!keyringListing.includes(`${ONEPASSWORD_GPG_KEY_FINGERPRINT}:`)) {
throw new Error(`bundled GPG key does not match expected fingerprint ${ONEPASSWORD_GPG_KEY_FINGERPRINT}.`);
}
// Verify op.sig against op using the imported key.
await runGpg([...baseArgs, "--verify", sigPath, opPath]);
}
catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(`1Password CLI signature verification failed: ${message}. ` +
"If 1Password has rotated their GPG signing key, this action needs to be updated — please file an issue at https://github.com/1Password/load-secrets-action/issues.");
}
finally {
external_fs_.rmSync(gpgHome, { recursive: true, force: true });
}
};
;// CONCATENATED MODULE: ./src/op-cli-installer/github-action/cli-installer/linux.ts
class LinuxInstaller extends CliInstaller {
platform = "linux"; // Node.js platform identifier for Linux
constructor(version) {
@@ -35378,10 +35430,77 @@ class LinuxInstaller extends CliInstaller {
}
async installCli() {
const urlBuilder = cliUrlBuilder[this.platform];
await super.install(urlBuilder(this.version, this.arch));
await this.install(urlBuilder(this.version, this.arch));
}
async install(url) {
console.info(`Downloading 1Password CLI from: ${url}`);
const downloadPath = await downloadTool(url);
console.info("Installing 1Password CLI");
const extractedPath = await extractZip(downloadPath);
info("Verifying 1Password CLI signature");
await verifyLinuxSignature(external_path_.join(extractedPath, "op"), external_path_.join(extractedPath, "op.sig"));
info("1Password CLI signature verified");
addPath(extractedPath);
info("1Password CLI installed");
}
}
;// CONCATENATED MODULE: ./src/op-cli-installer/github-action/cli-installer/macos-signature.ts
const macos_signature_execFileAsync = (0,external_util_.promisify)(external_child_process_.execFile);
// See https://www.1password.dev/cli/verify.
const APPLE_DEVELOPER_TEAM_ID = "2BUA8C4S2C";
// Append-only: old certs stay listed so historical `op` versions still verify.
// See https://www.1password.dev/cli/verify.
const ALLOWED_MACOS_SIGNING_CERT_FINGERPRINTS = [
"CAB578061B0209FB70934DA344EF6FEBCD3279B1C074C54B0D7D555743B9D89F",
"141DD87B2B231211F1440849798007DF621DE6EB3DAB985BC964EE9704C4A1C1",
];
const defaultPkgutilRunner = async (pkgPath) => {
const { stdout } = await macos_signature_execFileAsync("pkgutil", [
"--check-signature",
pkgPath,
]);
return stdout;
};
// Returns just entry 1 (the signer cert) from the chain.
const extractSignerCertSection = (pkgutilOutput) => {
const chainStart = pkgutilOutput.indexOf("Certificate Chain:");
if (chainStart === -1) {
return null;
}
const chainBody = pkgutilOutput.slice(chainStart);
const secondCert = /\n\s*2\.\s/.exec(chainBody);
return secondCert ? chainBody.slice(0, secondCert.index) : chainBody;
};
const parseSignerFingerprint = (signerSection) => {
const match = /SHA256 Fingerprint:\s*\n((?:[ \t]+[0-9A-Fa-f ]+\n?)+)/.exec(signerSection);
const captured = match?.[1];
return captured ? captured.replace(/\s+/g, "").toUpperCase() : null;
};
// Hard-fails if the .pkg at pkgPath is not signed by AgileBits Inc.
// (2BUA8C4S2C) with a certificate on the allowlist above. Must run
// before any extraction of the .pkg contents.
const verifyMacOsPackageSignature = async (pkgPath, runPkgutil = defaultPkgutilRunner) => {
const stdout = await runPkgutil(pkgPath);
const signerSection = extractSignerCertSection(stdout);
if (!signerSection) {
throw new Error(`1Password CLI signature verification failed: could not locate certificate chain in pkgutil output.\npkgutil output:\n${stdout}`);
}
if (!signerSection.includes(`(${APPLE_DEVELOPER_TEAM_ID})`)) {
throw new Error(`1Password CLI signature verification failed: expected developer team ID ${APPLE_DEVELOPER_TEAM_ID} not found in signer certificate.\npkgutil output:\n${stdout}`);
}
const signerFingerprint = parseSignerFingerprint(signerSection);
if (!signerFingerprint) {
throw new Error(`1Password CLI signature verification failed: could not parse signer cert SHA-256 fingerprint.\npkgutil output:\n${stdout}`);
}
if (!ALLOWED_MACOS_SIGNING_CERT_FINGERPRINTS.includes(signerFingerprint)) {
throw new Error(`1Password CLI signature verification failed: signer cert SHA-256 fingerprint ${signerFingerprint} is not on the allowlist. ` +
"If 1Password has rotated their installer signing cert, this action needs to be updated — please file an issue at https://github.com/1Password/load-secrets-action/issues.");
}
};
;// CONCATENATED MODULE: ./src/op-cli-installer/github-action/cli-installer/macos.ts
@@ -35391,7 +35510,8 @@ class LinuxInstaller extends CliInstaller {
const execFileAsync = (0,external_util_.promisify)(external_child_process_.execFile);
const macos_execFileAsync = (0,external_util_.promisify)(external_child_process_.execFile);
class MacOsInstaller extends CliInstaller {
platform = "darwin"; // Node.js platform identifier for macOS
constructor(version) {
@@ -35407,8 +35527,11 @@ class MacOsInstaller extends CliInstaller {
const pkgPath = await downloadTool(downloadUrl);
const pkgWithExtension = `${pkgPath}.pkg`;
external_fs_.renameSync(pkgPath, pkgWithExtension);
info("Verifying 1Password CLI signature");
await verifyMacOsPackageSignature(pkgWithExtension);
info("1Password CLI signature verified");
const expandDir = "temp-pkg";
await execFileAsync("pkgutil", ["--expand", pkgWithExtension, expandDir]);
await macos_execFileAsync("pkgutil", ["--expand", pkgWithExtension, expandDir]);
const payloadPath = external_path_.join(expandDir, "op.pkg", "Payload");
console.info("Installing 1Password CLI");
const cliPath = await extractTar(payloadPath);
@@ -35419,11 +35542,60 @@ class MacOsInstaller extends CliInstaller {
}
}
;// CONCATENATED MODULE: ./src/op-cli-installer/github-action/cli-installer/windows-signature.ts
const windows_signature_execFileAsync = (0,external_util_.promisify)(external_child_process_.execFile);
// Identifying field of 1Password's Authenticode signing cert for op.exe.
// See https://www.1password.dev/cli/verify.
const WINDOWS_SIGNER_SUBJECT_CN = "Agilebits";
const defaultPowerShellRunner = async (script) => {
const { stdout } = await windows_signature_execFileAsync("powershell.exe", [
"-NoProfile",
"-NonInteractive",
"-Command",
script,
]);
return stdout;
};
// Verifies op.exe's Authenticode signature against 1Password's signing cert.
// Throws unless the signature is cryptographically valid and the signer is AgileBits.
const verifyAuthenticodeSignature = async (opExePath, runPowerShell = defaultPowerShellRunner) => {
const escapedPath = opExePath.replace(/'/g, "''");
const script = [
`$sig = Get-AuthenticodeSignature -FilePath '${escapedPath}'`,
`"Status=$($sig.Status)"`,
`"Subject=$($sig.SignerCertificate.Subject)"`,
].join("; ");
const output = await runPowerShell(script);
const outputLines = output.split("\n").map((l) => l.trim());
const fieldValue = (prefix) => {
const matchingLine = outputLines.find((l) => l.startsWith(prefix));
if (!matchingLine) {
return undefined;
}
return matchingLine.slice(prefix.length);
};
// Reject unsigned or tampered binaries.
const status = fieldValue("Status=");
if (status !== "Valid") {
throw new Error(`Authenticode status is ${status ?? "unknown"}, expected Valid.\nGet-AuthenticodeSignature output:\n${output}`);
}
// Confirm the signer is AgileBits, not some other publisher.
const subject = fieldValue("Subject=") ?? "";
if (!subject.includes(`CN=${WINDOWS_SIGNER_SUBJECT_CN}`)) {
throw new Error(`1Password CLI signature verification failed: signer Subject (${subject}) does not contain CN=${WINDOWS_SIGNER_SUBJECT_CN}. ` +
"If 1Password has rotated or renamed their signing identity, this action needs to be updated — please file an issue at https://github.com/1Password/load-secrets-action/issues.");
}
};
;// CONCATENATED MODULE: ./src/op-cli-installer/github-action/cli-installer/windows.ts
class WindowsInstaller extends CliInstaller {
platform = "win32"; // Node.js platform identifier for Windows
constructor(version) {
@@ -35442,6 +35614,9 @@ class WindowsInstaller extends CliInstaller {
external_fs_.renameSync(downloadPath, zipPath);
console.info("Installing 1Password CLI");
const extractedPath = await extractZip(zipPath);
info("Verifying 1Password CLI signature");
await verifyAuthenticodeSignature(external_path_.join(extractedPath, "op.exe"));
info("1Password CLI signature verified");
addPath(extractedPath);
info("1Password CLI installed");
}
+49
View File
@@ -0,0 +1,49 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
P4I7jHwe0BN3iNAG1nKbGg==
=+LeX
-----END PGP PUBLIC KEY BLOCK-----
@@ -1,12 +1,13 @@
import {
ONEPASSWORD_GPG_KEY_FINGERPRINT,
ONEPASSWORD_GPG_KEYSERVER,
verifyLinuxSignature,
} from "./linux-signature";
describe("verifyLinuxSignature", () => {
const OP_PATH = "/tmp/op";
const SIG_PATH = `${OP_PATH}.sig`;
const CORRECT_FPR = `fpr:::::::::${ONEPASSWORD_GPG_KEY_FINGERPRINT}:\n`;
const WRONG_FPR = `fpr:::::::::DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF:\n`;
const gpgRunner = (...responses: (string | Error)[]) => {
const runner = jest.fn<Promise<string>, [readonly string[]]>();
@@ -22,38 +23,34 @@ describe("verifyLinuxSignature", () => {
const subcommandsCalled = (runner: ReturnType<typeof gpgRunner>) =>
runner.mock.calls.map(([args]: [readonly string[]]) =>
args.find((a) => a === "--recv-keys" || a === "--verify"),
args.find(
(a) => a === "--import" || a === "--list-keys" || a === "--verify",
),
);
it("fetches the pinned key by fingerprint and verifies the signature", async () => {
const runner = gpgRunner("", "");
it("imports the bundled key and verifies the signature", async () => {
const runner = gpgRunner("", CORRECT_FPR, "");
await expect(
verifyLinuxSignature(OP_PATH, SIG_PATH, runner),
).resolves.toBeUndefined();
expect(subcommandsCalled(runner)).toEqual(["--recv-keys", "--verify"]);
const recvKeysArgs = runner.mock.calls[0]![0];
expect(recvKeysArgs).toEqual(
expect.arrayContaining([
"--keyserver",
ONEPASSWORD_GPG_KEYSERVER,
"--recv-keys",
ONEPASSWORD_GPG_KEY_FINGERPRINT,
]),
);
expect(subcommandsCalled(runner)).toEqual([
"--import",
"--list-keys",
"--verify",
]);
});
it("throws if recv-keys fails (e.g., wrong fingerprint or keyserver unreachable)", async () => {
const runner = gpgRunner(new Error("No data"));
it("throws and skips --verify when the imported key has the wrong fingerprint", async () => {
const runner = gpgRunner("", WRONG_FPR);
await expect(
verifyLinuxSignature(OP_PATH, SIG_PATH, runner),
).rejects.toThrow(/No data/);
expect(subcommandsCalled(runner)).toEqual(["--recv-keys"]);
).rejects.toThrow(/does not match expected/);
expect(subcommandsCalled(runner)).toEqual(["--import", "--list-keys"]);
});
it("throws if gpg --verify rejects the signature", async () => {
const runner = gpgRunner("", new Error("BAD signature"));
it("throws when gpg --verify rejects the signature", async () => {
const runner = gpgRunner("", CORRECT_FPR, new Error("BAD signature"));
await expect(
verifyLinuxSignature(OP_PATH, SIG_PATH, runner),
).rejects.toThrow(/BAD signature/);
@@ -6,12 +6,18 @@ import { promisify } from "util";
const execFileAsync = promisify(execFile);
// 1Password's code-signing GPG key fingerprint. Used to verify the detached
// `op.sig` inside the Linux release zip.
// See https://www.1password.dev/cli/verify.
// 1Password's code-signing GPG key fingerprint. See
// https://www.1password.dev/cli/verify.
export const ONEPASSWORD_GPG_KEY_FINGERPRINT =
"3FEF9748469ADBE15DA7CA80AC2D62742012EA22";
export const ONEPASSWORD_GPG_KEYSERVER = "keyserver.ubuntu.com";
// Bundled 1Password code-signing public key `linux-signing-key.asc` in
// this directory. Bundled to avoid a runtime keyserver/URL dependency.
// Source: https://downloads.1password.com/linux/keys/1password.asc
const ONEPASSWORD_GPG_PUBLIC_KEY_PATH = path.join(
__dirname,
"linux-signing-key.asc",
);
const defaultGpgRunner = async (args: readonly string[]): Promise<string> => {
const { stdout } = await execFileAsync("gpg", args);
@@ -19,10 +25,7 @@ const defaultGpgRunner = async (args: readonly string[]): Promise<string> => {
};
// Throws unless the binary at opPath carries a valid GPG signature (at
// sigPath) from the pinned 1Password key.
//
// gpg --keyserver keyserver.ubuntu.com --recv-keys <fingerprint>
// gpg --verify <sigPath> <opPath>
// sigPath) from the pinned 1Password key. The key is bundled with the action
export const verifyLinuxSignature = async (
opPath: string,
sigPath: string,
@@ -32,17 +35,29 @@ export const verifyLinuxSignature = async (
try {
const baseArgs = ["--homedir", gpgHome, "--batch", "--no-tty"];
// Fetch the 1Password public key by fingerprint. gpg only accepts a
// key whose fingerprint matches the requested value.
await runGpg([
...baseArgs,
"--keyserver",
ONEPASSWORD_GPG_KEYSERVER,
"--recv-keys",
ONEPASSWORD_GPG_KEY_FINGERPRINT,
]);
// Import the bundled key into the temp keyring.
await runGpg([...baseArgs, "--import", ONEPASSWORD_GPG_PUBLIC_KEY_PATH]);
// Confirm we imported the pinned key.
const keyringListing = await runGpg([
...baseArgs,
"--list-keys",
"--with-colons",
]);
if (!keyringListing.includes(`${ONEPASSWORD_GPG_KEY_FINGERPRINT}:`)) {
throw new Error(
`bundled GPG key does not match expected fingerprint ${ONEPASSWORD_GPG_KEY_FINGERPRINT}.`,
);
}
// Verify op.sig against op using the imported key.
await runGpg([...baseArgs, "--verify", sigPath, opPath]);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(
`1Password CLI signature verification failed: ${message}. ` +
"If 1Password has rotated their GPG signing key, this action needs to be updated — please file an issue at https://github.com/1Password/load-secrets-action/issues.",
);
} finally {
fs.rmSync(gpgHome, { recursive: true, force: true });
}
@@ -0,0 +1,49 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
P4I7jHwe0BN3iNAG1nKbGg==
=+LeX
-----END PGP PUBLIC KEY BLOCK-----
@@ -18,8 +18,7 @@ const defaultPowerShellRunner = async (script: string): Promise<string> => {
};
// Verifies op.exe's Authenticode signature against 1Password's signing cert.
// Throws unless the signature is cryptographically valid and the signer is
// AgileBits.
// Throws unless the signature is cryptographically valid and the signer is AgileBits.
export const verifyAuthenticodeSignature = async (
opExePath: string,
runPowerShell: (script: string) => Promise<string> = defaultPowerShellRunner,
@@ -54,7 +53,8 @@ export const verifyAuthenticodeSignature = async (
const subject = fieldValue("Subject=") ?? "";
if (!subject.includes(`CN=${WINDOWS_SIGNER_SUBJECT_CN}`)) {
throw new Error(
`signer Subject (${subject}) does not contain CN=${WINDOWS_SIGNER_SUBJECT_CN}.`,
`1Password CLI signature verification failed: signer Subject (${subject}) does not contain CN=${WINDOWS_SIGNER_SUBJECT_CN}. ` +
"If 1Password has rotated or renamed their signing identity, this action needs to be updated — please file an issue at https://github.com/1Password/load-secrets-action/issues.",
);
}
};