From 7f1aa5edc1904d5ab2a0d0ec79ee722d7a50d480 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 6 May 2020 00:10:34 +0200 Subject: [PATCH] Use gpg-connect-agent to seed the internal cache of gpg-agent Fix keygrip (#10) --- __tests__/gpg.test.ts | 2 +- dist/index.js | 49 ++++++++++++++----------------------- src/gpg.ts | 57 +++++++++++++++---------------------------- 3 files changed, 38 insertions(+), 70 deletions(-) diff --git a/__tests__/gpg.test.ts b/__tests__/gpg.test.ts index 4387105..7722f5f 100644 --- a/__tests__/gpg.test.ts +++ b/__tests__/gpg.test.ts @@ -6,7 +6,7 @@ const userInfo = { passphrase: 'with stupid passphrase', keyID: 'D523BD50DD70B0BA', fingerprint: '27571A53B86AF0C799B38BA77D851EB72D73BDA0', - keygrip: 'BA83FC8947213477F28ADC019F6564A956456163', + keygrip: '3E2D1142AA59E08E16B7E2C64BA6DDC773B1A627', pgp: `-----BEGIN PGP PRIVATE KEY BLOCK----- lQdGBF6tzaABEACjFbX7PFEG6vDPN2MPyxYW7/3o/sonORj4HXUFjFxxJxktJ3x3 diff --git a/dist/index.js b/dist/index.js index 955aca1..0b9c5a0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1139,20 +1139,25 @@ const exec = __importStar(__webpack_require__(807)); exports.agentConfig = `default-cache-ttl 7200 max-cache-ttl 31536000 allow-preset-passphrase`; -const getGpgPresetPassphrasePath = () => __awaiter(void 0, void 0, void 0, function* () { - const { libexecdir: libexecdir } = yield exports.getDirs(); - let gpgPresetPassphrasePath = path.join(libexecdir, 'gpg-preset-passphrase'); - if (yield fs.existsSync(gpgPresetPassphrasePath)) { - return gpgPresetPassphrasePath; - } - return 'gpg-preset-passphrase'; -}); const getGnupgHome = () => __awaiter(void 0, void 0, void 0, function* () { if (process.env.GNUPGHOME) { return process.env.GNUPGHOME; } return path.join(process.env.HOME || '', '.gnupg'); }); +const gpgConnectAgent = (command) => __awaiter(void 0, void 0, void 0, function* () { + return yield exec.exec(`gpg-connect-agent "${command}" /bye`, [], true).then(res => { + if (res.stderr != '' && !res.success) { + throw new Error(res.stderr); + } + for (let line of res.stdout.replace(/\r/g, '').trim().split(/\n/g)) { + if (line.startsWith('ERR')) { + throw new Error(line); + } + } + return res.stdout.trim(); + }); +}); exports.getVersion = () => __awaiter(void 0, void 0, void 0, function* () { return yield exec.exec('gpg', ['--version'], true).then(res => { if (res.stderr != '') { @@ -1238,6 +1243,7 @@ exports.getKeygrip = (fingerprint) => __awaiter(void 0, void 0, void 0, function for (let line of res.stdout.replace(/\r/g, '').trim().split(/\n/g)) { if (line.startsWith('grp')) { keygrip = line.replace(/(grp|:)/g, '').trim(); + break; } } return keygrip; @@ -1249,31 +1255,12 @@ exports.configureAgent = (config) => __awaiter(void 0, void 0, void 0, function* if (err) throw err; }); - yield exec.exec(`gpg-connect-agent "RELOADAGENT" /bye`, [], true).then(res => { - if (res.stderr != '' && !res.success) { - throw new Error(res.stderr); - } - }); + yield gpgConnectAgent('RELOADAGENT'); }); exports.presetPassphrase = (keygrip, passphrase) => __awaiter(void 0, void 0, void 0, function* () { - yield exec - .exec(`"${yield getGpgPresetPassphrasePath()}" --verbose --preset --passphrase "${passphrase}" ${keygrip}`, [], true) - .then(res => { - if (res.stderr != '' && !res.success) { - throw new Error(res.stderr); - } - }); - return yield exec.exec(`gpg-connect-agent "KEYINFO ${keygrip}" /bye`, [], true).then(res => { - if (res.stderr != '' && !res.success) { - throw new Error(res.stderr); - } - for (let line of res.stdout.replace(/\r/g, '').trim().split(/\n/g)) { - if (line.startsWith('ERR')) { - throw new Error(line); - } - } - return res.stdout.trim(); - }); + const hexPassphrase = Buffer.from(passphrase, 'utf8').toString('hex').toUpperCase(); + yield gpgConnectAgent(`PRESET_PASSPHRASE ${keygrip} -1 ${hexPassphrase}`); + return yield gpgConnectAgent(`KEYINFO ${keygrip}`); }); exports.deleteKey = (fingerprint) => __awaiter(void 0, void 0, void 0, function* () { yield exec.exec('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], true).then(res => { diff --git a/src/gpg.ts b/src/gpg.ts index 0c2cf1e..08576b6 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -19,15 +19,6 @@ export interface Dirs { homedir: string; } -const getGpgPresetPassphrasePath = async (): Promise => { - const {libexecdir: libexecdir} = await getDirs(); - let gpgPresetPassphrasePath = path.join(libexecdir, 'gpg-preset-passphrase'); - if (await fs.existsSync(gpgPresetPassphrasePath)) { - return gpgPresetPassphrasePath; - } - return 'gpg-preset-passphrase'; -}; - const getGnupgHome = async (): Promise => { if (process.env.GNUPGHOME) { return process.env.GNUPGHOME; @@ -35,6 +26,20 @@ const getGnupgHome = async (): Promise => { return path.join(process.env.HOME || '', '.gnupg'); }; +const gpgConnectAgent = async (command: string): Promise => { + return await exec.exec(`gpg-connect-agent "${command}" /bye`, [], true).then(res => { + if (res.stderr != '' && !res.success) { + throw new Error(res.stderr); + } + for (let line of res.stdout.replace(/\r/g, '').trim().split(/\n/g)) { + if (line.startsWith('ERR')) { + throw new Error(line); + } + } + return res.stdout.trim(); + }); +}; + export const getVersion = async (): Promise => { return await exec.exec('gpg', ['--version'], true).then(res => { if (res.stderr != '') { @@ -125,6 +130,7 @@ export const getKeygrip = async (fingerprint: string): Promise => { for (let line of res.stdout.replace(/\r/g, '').trim().split(/\n/g)) { if (line.startsWith('grp')) { keygrip = line.replace(/(grp|:)/g, '').trim(); + break; } } return keygrip; @@ -136,38 +142,13 @@ export const configureAgent = async (config: string): Promise => { await fs.writeFile(gpgAgentConf, config, function (err) { if (err) throw err; }); - - await exec.exec(`gpg-connect-agent "RELOADAGENT" /bye`, [], true).then(res => { - if (res.stderr != '' && !res.success) { - throw new Error(res.stderr); - } - }); + await gpgConnectAgent('RELOADAGENT'); }; export const presetPassphrase = async (keygrip: string, passphrase: string): Promise => { - await exec - .exec( - `"${await getGpgPresetPassphrasePath()}" --verbose --preset --passphrase "${passphrase}" ${keygrip}`, - [], - true - ) - .then(res => { - if (res.stderr != '' && !res.success) { - throw new Error(res.stderr); - } - }); - - return await exec.exec(`gpg-connect-agent "KEYINFO ${keygrip}" /bye`, [], true).then(res => { - if (res.stderr != '' && !res.success) { - throw new Error(res.stderr); - } - for (let line of res.stdout.replace(/\r/g, '').trim().split(/\n/g)) { - if (line.startsWith('ERR')) { - throw new Error(line); - } - } - return res.stdout.trim(); - }); + const hexPassphrase: string = Buffer.from(passphrase, 'utf8').toString('hex').toUpperCase(); + await gpgConnectAgent(`PRESET_PASSPHRASE ${keygrip} -1 ${hexPassphrase}`); + return await gpgConnectAgent(`KEYINFO ${keygrip}`); }; export const deleteKey = async (fingerprint: string): Promise => {