2020-06-25 10:01:06 -06:00
|
|
|
async function verifySignature(opts) {
|
2020-06-26 05:06:32 -06:00
|
|
|
// Init
|
2020-06-25 10:01:06 -06:00
|
|
|
const elRes = document.body.querySelector("#result");
|
|
|
|
const elResContent = document.body.querySelector("#resultContent");
|
2020-06-26 05:06:32 -06:00
|
|
|
let keyData, feedback, signature, verified, valid;
|
2020-06-25 10:01:06 -06:00
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Reset feedback
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.innerHTML = "";
|
|
|
|
elResContent.innerHTML = "";
|
|
|
|
|
|
|
|
try {
|
2020-06-26 05:06:32 -06:00
|
|
|
// Get key data
|
2020-06-26 04:53:12 -06:00
|
|
|
keyData = await fetchKeys(opts);
|
2020-06-25 10:01:06 -06:00
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Handle missing signature
|
|
|
|
if (opts.signature == null) { throw("No signature was provided."); }
|
2020-06-25 10:01:06 -06:00
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Try two different methods of signature reading
|
2020-06-25 10:01:06 -06:00
|
|
|
let readError = null;
|
|
|
|
try {
|
|
|
|
signature = await openpgp.message.readArmored(opts.signature);
|
|
|
|
} catch(e) {
|
|
|
|
readError = e;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
signature = await openpgp.cleartext.readArmored(opts.signature);
|
|
|
|
} catch(e) {
|
|
|
|
readError = e;
|
|
|
|
}
|
2020-06-26 05:06:32 -06:00
|
|
|
if (signature == null) { throw(readError) };
|
2020-06-25 10:01:06 -06:00
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Verify the signature
|
2020-06-25 10:01:06 -06:00
|
|
|
verified = await openpgp.verify({
|
|
|
|
message: signature,
|
2020-06-26 04:53:12 -06:00
|
|
|
publicKeys: keyData.publicKey
|
2020-06-25 10:01:06 -06:00
|
|
|
});
|
2020-06-26 05:06:32 -06:00
|
|
|
{ valid } = verified.signatures[0];
|
2020-06-25 10:01:06 -06:00
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
elRes.innerHTML = e;
|
|
|
|
elRes.classList.remove('green');
|
|
|
|
elRes.classList.add('red');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Init feedback to empty string
|
2020-06-25 10:01:06 -06:00
|
|
|
feedback = '';
|
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// If content was extracted from signature
|
2020-06-26 04:53:12 -06:00
|
|
|
if (keyData.sigContent) {
|
2020-06-25 10:01:06 -06:00
|
|
|
elResContent.innerHTML = "<strong>Signature content:</strong><br><span style=\"white-space: pre-line\">"+sigContent+"</span>";
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Provide different feedback depending on key input mode
|
2020-06-26 04:53:12 -06:00
|
|
|
if (opts.mode == "signature" && keyData.sigUserId) {
|
2020-06-25 10:01:06 -06:00
|
|
|
if (valid) {
|
|
|
|
feedback += "The message was signed by the userId extracted from the signature.<br>";
|
2020-06-26 04:53:12 -06:00
|
|
|
feedback += 'UserId: '+keyData.sigUserId+'<br>';
|
|
|
|
feedback += "Fingerprint: "+keyData.fingerprint+"<br>";
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.classList.remove('red');
|
|
|
|
elRes.classList.add('green');
|
|
|
|
} else {
|
|
|
|
feedback += "The message's signature COULD NOT BE verified using the userId extracted from the signature.<br>";
|
2020-06-26 04:58:02 -06:00
|
|
|
feedback += 'UserId: '+keyData.sigUserId+'<br>';
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.classList.remove('green');
|
|
|
|
elRes.classList.add('red');
|
|
|
|
}
|
2020-06-26 04:53:12 -06:00
|
|
|
} else if (opts.mode == "signature" && keyData.sigKeyId) {
|
2020-06-25 10:01:06 -06:00
|
|
|
if (valid) {
|
|
|
|
feedback += "The message was signed by the keyId extracted from the signature.<br>";
|
2020-06-26 04:53:12 -06:00
|
|
|
feedback += 'KeyID: '+keyData.sigKeyId+'<br>';
|
|
|
|
feedback += "Fingerprint: "+keyData.fingerprint+"<br><br>";
|
2020-06-25 10:01:06 -06:00
|
|
|
feedback += "!!! You should manually verify the fingerprint to confirm the signer's identity !!!";
|
|
|
|
elRes.classList.remove('red');
|
|
|
|
elRes.classList.add('green');
|
|
|
|
} else {
|
|
|
|
feedback += "The message's signature COULD NOT BE verified using the keyId extracted from the signature.<br>";
|
2020-06-26 04:58:02 -06:00
|
|
|
feedback += 'KeyID: '+keyData.sigKeyId+'<br>';
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.classList.remove('green');
|
|
|
|
elRes.classList.add('red');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (valid) {
|
|
|
|
feedback += "The message was signed by the provided key ("+opts.mode+").<br>";
|
2020-06-26 04:53:12 -06:00
|
|
|
feedback += "Fingerprint: "+keyData.fingerprint+"<br>";
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.classList.remove('red');
|
|
|
|
elRes.classList.add('green');
|
|
|
|
} else {
|
|
|
|
feedback += "The message's signature COULD NOT BE verified using the provided key ("+opts.mode+").<br>";
|
|
|
|
elRes.classList.remove('green');
|
|
|
|
elRes.classList.add('red');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Display feedback
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.innerHTML = feedback;
|
|
|
|
};
|
|
|
|
|
|
|
|
async function encryptMessage(opts) {
|
2020-06-26 05:06:32 -06:00
|
|
|
// Init
|
2020-06-25 10:01:06 -06:00
|
|
|
const elEnc = document.body.querySelector("#messageEncrypted");
|
|
|
|
const elRes = document.body.querySelector("#result");
|
2020-06-26 04:53:12 -06:00
|
|
|
let keyData, feedback, message, encrypted;
|
2020-06-25 10:01:06 -06:00
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Reset feedback
|
2020-06-25 10:01:06 -06:00
|
|
|
elRes.innerHTML = "";
|
|
|
|
elEnc.value = "";
|
|
|
|
|
|
|
|
try {
|
2020-06-26 05:06:32 -06:00
|
|
|
// Get key data
|
2020-06-26 04:53:12 -06:00
|
|
|
keyData = await fetchKeys(opts);
|
2020-06-25 10:01:06 -06:00
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Handle missing message
|
2020-06-25 10:01:06 -06:00
|
|
|
if (opts.message == null) {
|
2020-06-26 05:06:32 -06:00
|
|
|
throw("No message was provided.");
|
2020-06-25 10:01:06 -06:00
|
|
|
}
|
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Encrypt the message
|
2020-06-25 10:01:06 -06:00
|
|
|
encrypted = await openpgp.encrypt({
|
|
|
|
message: openpgp.message.fromText(opts.message),
|
2020-06-26 04:53:12 -06:00
|
|
|
publicKeys: keyData.publicKey
|
2020-06-25 10:01:06 -06:00
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
elRes.innerHTML = e;
|
|
|
|
elRes.classList.remove('green');
|
|
|
|
elRes.classList.add('red');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-26 05:06:32 -06:00
|
|
|
// Display encrypted data
|
2020-06-25 10:01:06 -06:00
|
|
|
elEnc.value = encrypted.data;
|
|
|
|
};
|
|
|
|
|
2020-06-26 04:53:12 -06:00
|
|
|
async function fetchKeys(opts) {
|
|
|
|
let lookupOpts, wkd, hkd, sig, lastPrimarySig;
|
|
|
|
let output = {
|
|
|
|
publicKey: null,
|
|
|
|
user: null,
|
|
|
|
notations: null,
|
|
|
|
sigKeyId: null,
|
|
|
|
sigUserId: null,
|
|
|
|
sigContent: null
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (opts.mode) {
|
|
|
|
case "plaintext":
|
|
|
|
output.publicKey = (await openpgp.key.readArmored(opts.input)).keys[0];
|
|
|
|
|
|
|
|
if (!output.publicKey) {
|
|
|
|
throw("Error: No public keys could be fetched from the plaintext input.");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "wkd":
|
|
|
|
wkd = new openpgp.WKD();
|
|
|
|
lookupOpts = {
|
|
|
|
email: opts.input
|
|
|
|
};
|
|
|
|
output.publicKey = (await wkd.lookup(lookupOpts)).keys[0];
|
|
|
|
|
|
|
|
if (!output.publicKey) {
|
|
|
|
throw("Error: No public keys could be fetched using WKD.");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "hkp":
|
|
|
|
if (!opts.server) {opts.server = "https://keys.openpgp.org/"};
|
|
|
|
hkp = new openpgp.HKP(opts.server);
|
|
|
|
lookupOpts = {
|
|
|
|
query: opts.input
|
|
|
|
};
|
|
|
|
output.publicKey = await hkp.lookup(lookupOpts);
|
|
|
|
output.publicKey = (await openpgp.key.readArmored(output.publicKey)).keys[0];
|
|
|
|
|
|
|
|
if (!output.publicKey) {
|
|
|
|
throw("Error: No public keys could be fetched from the HKP server.");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "signature":
|
|
|
|
sig = (await openpgp.signature.readArmored(opts.signature));
|
|
|
|
if ('compressed' in sig.packets[0]) {
|
|
|
|
sig = sig.packets[0];
|
|
|
|
output.sigContent = (await openpgp.stream.readToEnd(await sig.packets[1].getText()));
|
|
|
|
};
|
|
|
|
output.sigUserId = sig.packets[0].signersUserId;
|
|
|
|
output.sigKeyId = (await sig.packets[0].issuerKeyId.toHex());
|
|
|
|
|
|
|
|
if (!opts.server) {opts.server = "https://keys.openpgp.org/"};
|
|
|
|
hkp = new openpgp.HKP(opts.server);
|
|
|
|
lookupOpts = {
|
|
|
|
query: output.sigUserId ? output.sigUserId : output.sigKeyId
|
|
|
|
};
|
|
|
|
output.publicKey = await hkp.lookup(lookupOpts);
|
|
|
|
output.publicKey = (await openpgp.key.readArmored(output.publicKey)).keys[0];
|
|
|
|
|
|
|
|
if (!output.publicKey) {
|
|
|
|
throw("Error: No public keys could be extracted from the signature.");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
output.fingerprint = output.publicKey.primaryKey.getFingerprint();
|
|
|
|
output.user = await output.publicKey.getPrimaryUser();
|
|
|
|
lastPrimarySig = output.user.selfCertification;
|
|
|
|
output.notations = lastPrimarySig.notations || [];
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
// General purpose
|
2020-06-25 10:01:06 -06:00
|
|
|
let elFormVerify = document.body.querySelector("#form-verify"),
|
|
|
|
elFormEncrypt = document.body.querySelector("#form-encrypt");
|
|
|
|
|
|
|
|
if (elFormVerify) {
|
|
|
|
elFormVerify.onsubmit = function (evt) {
|
|
|
|
evt.preventDefault();
|
|
|
|
|
|
|
|
let opts = {
|
|
|
|
signature: null,
|
|
|
|
mode: null,
|
|
|
|
input: null,
|
|
|
|
server: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
opts.signature = document.body.querySelector("#signature").value;
|
|
|
|
|
|
|
|
if (document.body.querySelector("#publicKey").value != "") {
|
|
|
|
opts.input = document.body.querySelector("#publicKey").value;
|
|
|
|
opts.mode = "plaintext";
|
|
|
|
} else if (document.body.querySelector("#wkd").value != "") {
|
|
|
|
opts.input = document.body.querySelector("#wkd").value;
|
|
|
|
opts.mode = "wkd";
|
|
|
|
} else if (document.body.querySelector("#hkp_input").value != "") {
|
|
|
|
opts.input = document.body.querySelector("#hkp_input").value;
|
|
|
|
opts.server = document.body.querySelector("#hkp_server").value;
|
|
|
|
opts.mode = "hkp";
|
2020-06-26 04:53:12 -06:00
|
|
|
} else {
|
|
|
|
opts.mode = "signature";
|
2020-06-25 10:01:06 -06:00
|
|
|
}
|
|
|
|
verifySignature(opts);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elFormEncrypt) {
|
|
|
|
elFormEncrypt.onsubmit = function (evt) {
|
|
|
|
evt.preventDefault();
|
|
|
|
|
|
|
|
let opts = {
|
|
|
|
message: null,
|
|
|
|
mode: null,
|
|
|
|
input: null,
|
|
|
|
server: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
opts.message = document.body.querySelector("#message").value;
|
|
|
|
|
|
|
|
if (document.body.querySelector("#publicKey").value != "") {
|
|
|
|
opts.input = document.body.querySelector("#publicKey").value;
|
|
|
|
opts.mode = "plaintext";
|
|
|
|
} else if (document.body.querySelector("#wkd").value != "") {
|
|
|
|
opts.input = document.body.querySelector("#wkd").value;
|
|
|
|
opts.mode = "wkd";
|
|
|
|
} else if (document.body.querySelector("#hkp_input").value != "") {
|
|
|
|
opts.input = document.body.querySelector("#hkp_input").value;
|
|
|
|
opts.server = document.body.querySelector("#hkp_server").value;
|
|
|
|
opts.mode = "hkp";
|
2020-06-26 04:53:12 -06:00
|
|
|
} else {
|
|
|
|
opts.mode = "signature";
|
2020-06-25 10:01:06 -06:00
|
|
|
}
|
|
|
|
encryptMessage(opts);
|
|
|
|
};
|
|
|
|
}
|