Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Custom pinentry wrapper for GPG #846

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c969587
Rewrite GPG wrapper to use a dedicated agent and pinentry script
smashwilson May 18, 2017
6e753d5
Use `/tmp` as a temp folder path to avoid too-long socket paths
smashwilson May 18, 2017
a2eabc4
Updoot GPG tests
smashwilson May 18, 2017
665427d
Default empty options to getTempDir
smashwilson May 18, 2017
55083ab
Begin porting gpg-wrapper to Node
smashwilson Jun 7, 2017
b83c394
Copy the GPG wrapper into the prompt server temp directory
smashwilson Jun 8, 2017
5b595a1
Delete temp folders at process exit (if not before)
smashwilson Jun 8, 2017
dd4f14f
Change gpgExec to a regular prompt server call
smashwilson Jun 8, 2017
a8e2a76
Report GPG logs, if present, after git operations
smashwilson Jun 8, 2017
81c80ce
Construct GPG args per-call
smashwilson Jun 8, 2017
314c783
Refine spawn() call for gpg-agent
smashwilson Jun 8, 2017
e4c5411
Move process.exit() into cleanup
smashwilson Jun 8, 2017
1f8d23a
Recursively copy gpg home
smashwilson Jun 8, 2017
c530c25
Pass more environment to GPG agent and GPG
smashwilson Jun 8, 2017
93c9bb7
Placeholder TODO
smashwilson Jun 8, 2017
a5dc472
Unnecessary arrow function
smashwilson Jun 8, 2017
44f2275
Provide more context on errors raised by runGpgProgram
smashwilson Jun 8, 2017
041c9a9
Attempt gpg with native pinentry first
smashwilson Jun 8, 2017
0c9086e
Port gpg-pinentry to JavaScript
smashwilson Jun 8, 2017
82d4361
bash :point_right: sh
smashwilson Jun 8, 2017
96f913f
Super ultra async await powers: Activate
smashwilson Jun 8, 2017
bfd2dd2
Reorganize function definitions
smashwilson Jun 8, 2017
46f4f78
Reorganize function calls
smashwilson Jun 8, 2017
9cb8239
[refactoring intensifies]
smashwilson Jun 9, 2017
b4534d3
gpg isn't executed through a shell so gpg.program is unnormalized
smashwilson Jun 9, 2017
0349895
'' is falsy
smashwilson Jun 9, 2017
68e0aef
Detect a gpg4win home directory in %APPDATA%
smashwilson Jun 9, 2017
cfa4887
git config adds a newline on Windows
smashwilson Jun 9, 2017
b7b6654
Refactor runGpgProgram to accept an options argument
smashwilson Jun 9, 2017
d82980f
Reformat askpass script slightly
smashwilson Jun 9, 2017
7243320
Consistent variable order :art:
smashwilson Jun 9, 2017
f09d392
Attempt to use --passphrase-fd if the agent can't be launched
smashwilson Jun 9, 2017
cb77810
Default to a path lookup for gpg-agent
smashwilson Jun 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion bin/git-askpass-atom.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
#!/bin/sh
ELECTRON_RUN_AS_NODE=1 ELECTRON_NO_ATTACH_CONSOLE=1 "$ATOM_GITHUB_ELECTRON_PATH" "$ATOM_GITHUB_ASKPASS_PATH" "$ATOM_GITHUB_SOCK_PATH" "$@"

export ELECTRON_RUN_AS_NODE=1
export ELECTRON_NO_ATTACH_CONSOLE=1

exec "$ATOM_GITHUB_ELECTRON_PATH" "$ATOM_GITHUB_ASKPASS_PATH" "$ATOM_GITHUB_SOCK_PATH" "$@"
24 changes: 0 additions & 24 deletions bin/gpg-no-tty.sh

This file was deleted.

172 changes: 172 additions & 0 deletions bin/gpg-pinentry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
const path = require('path');
const fs = require('fs');
const net = require('net');
const readline = require('readline');

const diagnosticsEnabled = process.env.GIT_TRACE && process.env.GIT_TRACE.length !== 0;
const socketPath = process.env.ATOM_GITHUB_SOCK_PATH;

let logStream = null;

const INFO = {
flavor: 'atom:atom',
version: '0.0.0',
pid: process.pid,
};

async function main() {
let exitCode = 1;
try {
process.stdout.write('OK Your orders please\n');
exitCode = await parseStdin();
} catch (err) {
log(`Failed with error:\n${err}`);
} finally {
await cleanup();
process.exit(exitCode);
}
}

function parseStdin() {
return new Promise((resolve, reject) => {
const rl = readline.createInterface({input: process.stdin});
const state = {
desc: 'Please enter the passphrase for your default GPG signing key.',
error: null,
exit: null,
};

rl.on('line', async line => {
Object.assign(state, await handleCommand(line, state));
if (state.exit !== null) {
resolve(state.exit);
}
});

rl.on('close', () => {
if (state.exit !== null) {
state.exit = 1;
resolve(state.exit);
}
});
});
}

function handleDefault() {
process.stdout.write('OK\n');
return {};
}

function handleGetInfo(param) {
const reply = INFO[param];
if (reply) {
process.stdout.write(`D ${reply}\n`);
}
process.stdout.write('OK\n');
return {};
}

function handleSetDesc(param) {
process.stdout.write('OK\n');
return {prompt: decodeURI(param)};
}

function handleSetError(param) {
process.stdout.write('OK\n');
return {error: decodeURI(param)};
}

async function handleGetPin(param, state) {
const passphrase = await dialog(state);
process.stdout.write(`D ${encodeURI(passphrase)}\nOK\n`);
return {};
}

function handleBye() {
return {exit: 0};
}

const COMMANDS = {
GETINFO: handleGetInfo,
SETDESC: handleSetDesc,
SETERROR: handleSetError,
GETPIN: handleGetPin,
BYE: handleBye,
};

function handleCommand(line, state) {
log(`Command: ${line}`);

let handler = handleDefault;
let param = null;
const match = /^(\S+)(?:\s+(.+))?/.exec(line);
if (match) {
handler = COMMANDS[match[1]] || handleDefault;
param = match[2];
}

return handler(param, state);
}

/*
* Request a dialog in Atom by writing a null-delimited JSON query to the socket.
*/
function dialog(state) {
const payload = {
error: state.error,
prompt: state.prompt,
includeUsername: false,
pid: process.pid,
};

return new Promise((resolve, reject) => {
log('Requesting dialog through Atom socket.');

const socket = net.connect(socketPath, () => {
log('Connection established.');

const parts = [];

socket.on('data', data => parts.push(data));
socket.on('end', () => {
log('Atom socket stream terminated.');
try {
const reply = JSON.parse(parts.join(''));
resolve(reply.password);
} catch (e) {
log(`Unable to parse reply from Atom:\n${parts.join('')}`);
reject(e);
}
});

log('Sending payload.');
socket.write(JSON.stringify(payload) + '\u0000', 'utf8');
log('Payload sent.');
});
socket.setEncoding('utf8');
});
}

/*
* Emit diagnostic messages to stderr if GIT_TRACE is set to a non-empty value.
*/
function log(message) {
if (!diagnosticsEnabled) {
return;
}

if (!logStream) {
const logFile = path.join(process.env.ATOM_GITHUB_TMP, 'gpg-pinentry.log');
logStream = fs.createWriteStream(logFile, {defaultEncoding: 'utf8'});
}

logStream.write(message + '\n');
}

async function cleanup() {
if (logStream) {
await new Promise(resolve => logStream.end('\n', 'utf8', resolve));
}
}

main();
6 changes: 6 additions & 0 deletions bin/gpg-pinentry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh

export ELECTRON_RUN_AS_NODE=1
export ELECTRON_NO_ATTACH_CONSOLE=1

exec "$ATOM_GITHUB_ELECTRON_PATH" "$ATOM_GITHUB_PINENTRY_PATH" "$@"
Loading