Skip to content

Commit

Permalink
Support special @ENV and @FD filenames
Browse files Browse the repository at this point in the history
  • Loading branch information
twiss committed Nov 18, 2024
1 parent 66b6ca0 commit 3a4b88b
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/commands/armor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ for (const maybePacket of Object.values(openpgp)) {
}

const armor = async () => {
let data = await utils.read_stdin();
let data = await utils.readStdin();
try {
// If the data is already armored, unarmor it first.
({ data } = await openpgp.unarmor(data));
Expand Down
2 changes: 1 addition & 1 deletion src/commands/dearmor.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const process = require('process');
const utils = require('../utils');

const dearmor = async () => {
const data = await utils.read_stdin();
const data = await utils.readStdin();

try {
const { data: unarmored } = await openpgp.unarmor(data);
Expand Down
19 changes: 9 additions & 10 deletions src/commands/decrypt.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { CANNOT_DECRYPT, BAD_DATA, KEY_IS_PROTECTED } = require('../errorCodes');

const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith, verificationsOut, keyfiles, withKeyPassword) => {
const encrypted = await utils.read_stdin();
const encrypted = await utils.readStdin();
let message;
try {
message = await openpgp.readMessage({ binaryMessage: encrypted });
Expand All @@ -23,7 +22,7 @@ const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith,
}

if (withPassword) {
const password = fs.readFileSync(withPassword);
const password = utils.readFile(withPassword);
const options = {
message: message,
passwords: password,
Expand All @@ -39,7 +38,7 @@ const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith,
}

if (withSessionKey) {
const sessionKeyEncoded = fs.readFileSync(withSessionKey, 'utf8');
const sessionKeyEncoded = utils.readFile(withSessionKey).toString('utf8');
const [algo, data] = sessionKeyEncoded.split(':');
const sessionKey = {
data: Buffer.from(data, 'hex'),
Expand All @@ -59,9 +58,9 @@ const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith,
return;
}

let decryptionKeys = await utils.load_keys(...keyfiles);
let decryptionKeys = await utils.loadKeys(...keyfiles);
if (withKeyPassword) {
const keyPassword = fs.readFileSync(withKeyPassword, 'utf8');
const keyPassword = utils.readFile(withKeyPassword).toString('utf8');
decryptionKeys = await Promise.all(decryptionKeys.map(privateKey => openpgp.decryptKey({
privateKey,
passphrase: [keyPassword, keyPassword.trimEnd()]
Expand Down Expand Up @@ -89,7 +88,7 @@ const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith,

let verificationKeys;
if (verifyWith.length) {
verificationKeys = await utils.load_certs(...verifyWith);
verificationKeys = await utils.loadCerts(...verifyWith);
options.verificationKeys = verificationKeys;
}

Expand All @@ -107,7 +106,7 @@ const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith,
}
if (verified) {
const signature = await s.signature;
const timestamp = utils.format_date(signature.packets[0].created);
const timestamp = utils.formatDate(signature.packets[0].created);
for (const cert of verificationKeys) {
const signKey = await cert.getSigningKey(s.keyID, null).catch(() => null);
if (signKey) {
Expand All @@ -121,15 +120,15 @@ const decrypt = async (withPassword, sessionKeyOut, withSessionKey, verifyWith,
}
}
}
fs.writeFileSync(verificationsOut, verifications);
utils.writeFile(verificationsOut, verifications);
}

if (sessionKeyOut) {
const { algorithm, data } = decryptedSessionKeys[0];
const sessionKeyEncoded =
openpgp.enums.write(openpgp.enums.symmetric, algorithm) +
':' + Buffer.from(data).toString('hex').toUpperCase();
fs.writeFileSync(sessionKeyOut, sessionKeyEncoded);
utils.writeFile(sessionKeyOut, sessionKeyEncoded);
}
}).catch((e) => {
console.error(e.message);
Expand Down
11 changes: 5 additions & 6 deletions src/commands/encrypt.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { CERT_CANNOT_ENCRYPT } = require('../errorCodes');

const encrypt = async (withPassword, signWith, withKeyPassword, certfiles, as, armor, profileOptions) => {
const data = await utils.read_stdin();
const data = await utils.readStdin();
const message = await openpgp.createMessage(
as === 'binary' ?
{ binary: data } :
{ text: data.toString('utf8') }
);
if (withPassword) {
const password = fs.readFileSync(withPassword);
const password = utils.readFile(withPassword);
const options = {
...profileOptions,
message,
Expand All @@ -28,13 +27,13 @@ const encrypt = async (withPassword, signWith, withKeyPassword, certfiles, as, a
const options = {
...profileOptions,
message,
encryptionKeys: await utils.load_certs(...certfiles),
encryptionKeys: await utils.loadCerts(...certfiles),
format: armor ? 'armored' : 'binary'
};
if (signWith.length) {
let signingKeys = await utils.load_keys(...signWith);
let signingKeys = await utils.loadKeys(...signWith);
if (withKeyPassword) {
const keyPassword = fs.readFileSync(withKeyPassword, 'utf8');
const keyPassword = utils.readFile(withKeyPassword).toString('utf8');
signingKeys = await Promise.all(signingKeys.map(privateKey => openpgp.decryptKey({
privateKey,
passphrase: [keyPassword, keyPassword.trimEnd()]
Expand Down
5 changes: 2 additions & 3 deletions src/commands/extract.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { BAD_DATA } = require('../errorCodes');

const extract = async (armor) => {
const [privateKey] = await utils.load_keys('/dev/stdin');
const [privateKey] = await utils.loadKeys('/dev/stdin');

try {
const pubKey = privateKey.toPublic();
if (!armor) {
fs.writeSync(1, pubKey.toPacketlist().write());
process.stdout.write(pubKey.toPacketlist().write());
return;
}
process.stdout.write(pubKey.armor());
Expand Down
4 changes: 2 additions & 2 deletions src/commands/generate.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { BAD_DATA } = require('../errorCodes');

const generateKey = async (withKeyPassword, armor, userids, profileOptions) => {
let passphrase;
if (withKeyPassword) {
passphrase = fs.readFileSync(withKeyPassword, 'utf8').trimEnd();
passphrase = utils.readFile(withKeyPassword).toString('utf8').trimEnd();
}
const options = {
...profileOptions,
Expand Down
5 changes: 2 additions & 3 deletions src/commands/inlineDetach.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { BAD_DATA } = require('../errorCodes');

const inlineDetach = async (signaturesOut, armor) => {
const data = await utils.read_stdin();
const data = await utils.readStdin();
let message;
try {
message = await openpgp.readMessage({ binaryMessage: data });
Expand Down Expand Up @@ -35,7 +34,7 @@ const inlineDetach = async (signaturesOut, armor) => {
}
if (signaturesOut) {
const signatures = new openpgp.Signature(signaturePackets)[armor ? 'armor' : 'write']();
fs.writeFileSync(signaturesOut, signatures);
utils.writeFile(signaturesOut, signatures);
}
process.stdout.write(sig.data);
}).catch((e) => {
Expand Down
7 changes: 3 additions & 4 deletions src/commands/inlineSign.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const utils = require('../utils');
const { KEY_CANNOT_SIGN } = require('../errorCodes');

const inlineSign = async (keyfiles, withKeyPassword, as, armor) => {
const data = await utils.read_stdin();
const data = await utils.readStdin();

const fn = as === 'clearsigned' ? 'createCleartextMessage' : 'createMessage';
const message = await openpgp[fn](
as === 'binary' ?
{ binary: data } :
{ text: data.toString('utf8') }
);
let signingKeys = await utils.load_keys(...keyfiles);
let signingKeys = await utils.loadKeys(...keyfiles);
if (withKeyPassword) {
const keyPassword = fs.readFileSync(withKeyPassword, 'utf8');
const keyPassword = utils.readFile(withKeyPassword).toString('utf8');
signingKeys = await Promise.all(signingKeys.map(privateKey => openpgp.decryptKey({
privateKey,
passphrase: [keyPassword, keyPassword.trimEnd()]
Expand Down
9 changes: 4 additions & 5 deletions src/commands/inlineVerify.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { NO_SIGNATURE, BAD_DATA } = require('../errorCodes');

const inlineVerify = async (certfiles, verificationsOut) => {
const verificationKeys = await utils.load_certs(...certfiles);
const data = await utils.read_stdin();
const verificationKeys = await utils.loadCerts(...certfiles);
const data = await utils.readStdin();
let message;
try {
message = await openpgp.readMessage({ binaryMessage: data });
Expand Down Expand Up @@ -42,7 +41,7 @@ const inlineVerify = async (certfiles, verificationsOut) => {
if (verified) {
count++;
const signature = await s.signature;
const timestamp = utils.format_date(signature.packets[0].created);
const timestamp = utils.formatDate(signature.packets[0].created);
for (const cert of verificationKeys) {
const [signingKey] = await cert.getKeys(s.keyID);
if (signingKey) {
Expand All @@ -60,7 +59,7 @@ const inlineVerify = async (certfiles, verificationsOut) => {
return process.exit(NO_SIGNATURE);
}
if (verificationsOut) {
fs.writeFileSync(verificationsOut, verifications);
utils.writeFile(verificationsOut, verifications);
}
process.stdout.write(sig.data);
}).catch((e) => {
Expand Down
7 changes: 3 additions & 4 deletions src/commands/sign.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const utils = require('../utils');
const { KEY_CANNOT_SIGN } = require('../errorCodes');

const sign = async (keyfiles, withKeyPassword, as, armor) => {
const data = await utils.read_stdin();
const data = await utils.readStdin();
const message = await openpgp.createMessage(
as === 'binary' ?
{ binary: data } :
{ text: data.toString('utf8') }
);

let signingKeys = await utils.load_keys(...keyfiles);
let signingKeys = await utils.loadKeys(...keyfiles);
if (withKeyPassword) {
const keyPassword = fs.readFileSync(withKeyPassword, 'utf8');
const keyPassword = utils.readFile(withKeyPassword).toString('utf8');
signingKeys = await Promise.all(signingKeys.map(privateKey => openpgp.decryptKey({
privateKey,
passphrase: [keyPassword, keyPassword.trimEnd()]
Expand Down
9 changes: 4 additions & 5 deletions src/commands/verify.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
const openpgp = require('../initOpenpgp');
const fs = require('fs');
const process = require('process');
const utils = require('../utils');
const { NO_SIGNATURE, BAD_DATA } = require('../errorCodes');

const verify = async (signature, certfiles) => {
const certs = await utils.load_certs(...certfiles);
const sigBuf = fs.readFileSync(signature);
const certs = await utils.loadCerts(...certfiles);
const sigBuf = utils.readFile(signature);
let sig;
try {
sig = await openpgp.readSignature({ binarySignature: sigBuf });
Expand All @@ -19,7 +18,7 @@ const verify = async (signature, certfiles) => {
}
}

const data = await utils.read_stdin();
const data = await utils.readStdin();

const options = {
message: await openpgp.createMessage({ text: data.toString('utf8') }),
Expand All @@ -40,7 +39,7 @@ const verify = async (signature, certfiles) => {
if (verified) {
count += 1;
const signature = await s.signature;
const timestamp = utils.format_date(signature.packets[0].created);
const timestamp = utils.formatDate(signature.packets[0].created);
for (const cert of certs) {
const [signingKey] = await cert.getKeys(s.keyID);
if (signingKey) {
Expand Down
41 changes: 30 additions & 11 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,28 @@ const streamConsumer = require('node:stream/consumers');
const { BAD_DATA, UNSUPPORTED_PROFILE } = require('./errorCodes');
const PROFILES = require('./profiles');

const load_certs = async (...filenames) => {
const readStdin = () => streamConsumer.buffer(process.stdin);

const readFile = (filename) => {
if (filename.startsWith('@ENV:')) {
return Buffer.from(process.env[filename.substr(5)]);
}
if (filename.startsWith('@FD:')) {
return fs.readFileSync(parseInt(filename.substr(4)));
}
return fs.readFileSync(filename);
}

const writeFile = (filename, contents) => {
if (filename.startsWith('@FD:')) {
return fs.writeFileSync(parseInt(filename.substr(4)), contents);
}
return fs.writeFileSync(filename, contents);
}

const loadCerts = async (...filenames) => {
return (await Promise.all(filenames.map(async filename => {
const buf = fs.readFileSync(filename);
const buf = readFile(filename);

let certs;
try {
Expand All @@ -25,9 +44,9 @@ const load_certs = async (...filenames) => {
}))).flat();
};

const load_keys = async (...filenames) => {
const loadKeys = async (...filenames) => {
return (await Promise.all(filenames.map(async filename => {
const buf = fs.readFileSync(filename);
const buf = readFile(filename);

let keys;
try {
Expand All @@ -45,10 +64,8 @@ const load_keys = async (...filenames) => {
}))).flat();
};

const read_stdin = () => streamConsumer.buffer(process.stdin);

// Emits a Date as specified in Section 5.9 of the SOP spec.
const format_date = (d) => {
const formatDate = (d) => {
return d.toISOString().replace(/\.\d{3}/, ''); // ISO 8601 format without milliseconds.
};

Expand All @@ -63,9 +80,11 @@ const getProfileOptions = (subcommand, profileName = 'default') => {
};

module.exports = {
load_certs,
load_keys,
read_stdin,
format_date,
readStdin,
readFile,
writeFile,
loadCerts,
loadKeys,
formatDate,
getProfileOptions
};

0 comments on commit 3a4b88b

Please sign in to comment.