scutum
Version:
Another OpenPGP command line tool like GPG.
190 lines (147 loc) • 5.54 kB
JavaScript
const openpgp = require("../openpgp");
const util = require("../util");
const io = require("../io");
const read_signatures = require("../io/read_signatures");
require("../router").register(
"verify [--not-before=DATE] [--not-after=DATE] [--] SIGNATURES CERTS [CERTS...]",
subcommand
);
/**
* Check a single verify result, as returned by OpenPGP.js, is acceptable
* according to our conditions. It must be valid by itself, and fall into
* data range defined by user.
*/
async function check_verification(verification, not_before, not_after){
if(verification.valid !== undefined && true !== verification.valid) return;
if(verification.verified !== undefined){
if(!(await verification.verified)) return;
}
const creation = verification.signature.packets[0].created;
const creation_timestamp = creation.getTime();
if(not_before != false && creation < not_before.getTime()) return;
if(not_after != false && creation > not_after.getTime()) return;
return { creation: creation };
}
/**
* Find out which key was used to generate a signature. Code copied from
* OpenPGP.js, at <https://github.com/openpgpjs/openpgpjs/blob/master/src/message.js>
*/
async function get_primary_and_signing_key(keys, issuer_key_id){
let signingKey = null, primaryKey = null;
await Promise.all(keys.map(async function(key) {
// Look for the unique key that matches issuerKeyId of signature
try {
signingKey = await key.getSigningKey(issuer_key_id, null);
primaryKey = key;
} catch(e) {
}
}));
return { signingKey, primaryKey };
}
async function subcommand(args, options){
const { stdin, stdout, stderr } = options;
const not_before = args["--not-before"],
not_after = args["--not-after"];
const inputs = [args.SIGNATURES].concat(args.CERTS);
const signatures = await read_signatures(inputs, function(sig){
return (sig !== undefined);
});
const public_keys = await io.keys.public_from_files(inputs);
// ensure all inputs are valid
if(signatures.length + public_keys.length != inputs.length){
stderr.throw("bad_data");
}
const data_to_verify = await util.stream_readall(stdin);
const message = await openpgp.message.fromBinary(data_to_verify);
let ever_accepted = false;
for(let signature of signatures){
// verify each provided signature file, against all public keys
// supplied
const verify_result = await openpgp.verify({
message: message,
signature: signature,
publicKeys: public_keys,
});
const verify_result_parsed = await read_verification(
public_keys,
verify_result.signatures,
not_before,
not_after
);
if(verify_result_parsed.length > 0) ever_accepted = true;
stdout(verifications_to_string(verify_result_parsed));
}
if(!ever_accepted){
stderr.throw("no_signature");
} else {
stderr.throw("ok");
}
}
/**
* Verify a message with given arguments to openpgp.verify, then interprete
* the results. Returns { creation, signing_key_fingerprint,
* primary_key_fingerprint } for each successful verified signature.
*
* @param {str} not_before - Optional. An ISO-8601 string indicating the
* earliest creation date at which a signature may be
* accepted. Defaults to start of time.
* @param {str} not_after - Optional. An ISO-8601 string indicating the
* latest creation date at which a signature may be
* accepted. Defaults to current time.
*/
async function read_verification(public_keys, verifications, not_before, not_after){
const ret = [];
let date_not_before = false,
date_not_after = new Date();
try{
if(null != not_before){
date_not_before = util.parse_date(not_before);
}
if(null != not_after){
date_not_after = util.parse_date(not_after);
}
} catch(e) {
throw Error(e);
}
// examine verifications result, each verification corresponding to a
// public key
for(let verification of verifications){
let check_result = await check_verification(
verification,
date_not_before,
date_not_after
);
if(!check_result) continue;
ever_accepted = true;
const creation = check_result.creation;
const issuer_key_id = verification.keyid;
const { signingKey, primaryKey } =
await get_primary_and_signing_key(
public_keys,
issuer_key_id
);
ret.push({
creation: creation,
signing_key_fingerprint: signingKey.getFingerprint().toUpperCase(),
primary_key_fingerprint: primaryKey.getFingerprint().toUpperCase(),
});
}
return ret;
}
function verifications_to_string(list){
const ret = [];
for(let {
creation,
signing_key_fingerprint,
primary_key_fingerprint
} of list){
ret.push([
creation.toISOString(),
signing_key_fingerprint,
primary_key_fingerprint,
].join(" "));
}
return ret.join("\n");
}
module.exports.read_verification = read_verification;
module.exports.verifications_to_string = verifications_to_string;