mirror of
https://codeberg.org/keyoxide/keyoxide-web.git
synced 2024-12-22 14:59:29 -07:00
Add proof verification page
This commit is contained in:
parent
7155e603f1
commit
8540fe9d9d
6 changed files with 267 additions and 0 deletions
|
@ -16,6 +16,7 @@
|
|||
<nav>
|
||||
<a href="/verify">verify</a>
|
||||
<a href="/encrypt">encrypt</a>
|
||||
<a href="/proofs">proofs</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
1
faq.html
1
faq.html
|
@ -16,6 +16,7 @@
|
|||
<nav>
|
||||
<a href="/verify">verify</a>
|
||||
<a href="/encrypt">encrypt</a>
|
||||
<a href="/proofs">proofs</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<nav>
|
||||
<a href="/verify">verify</a>
|
||||
<a href="/encrypt">encrypt</a>
|
||||
<a href="/proofs">proofs</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -27,6 +28,7 @@
|
|||
<p>
|
||||
<a class="bigBtn" href="/verify">verify signature</a>
|
||||
<a class="bigBtn" href="/encrypt">encrypt message</a>
|
||||
<a class="bigBtn" href="/proofs">verify proofs</a>
|
||||
</p>
|
||||
<h2>About</h2>
|
||||
<p><a href="/">Keyoxide</a> is a lightweight and FOSS solution to make basic cryptography operations accessible to regular humans.</p>
|
||||
|
|
63
proofs.html
Normal file
63
proofs.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Profile - Keyoxide</title>
|
||||
<script async defer data-domain="keyoxide.org" src="https://plausible.io/js/plausible.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.css">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<a href="/">Keyoxide</a>
|
||||
<div class="spacer"></div>
|
||||
<nav>
|
||||
<a href="/verify">verify</a>
|
||||
<a href="/encrypt">encrypt</a>
|
||||
<a href="/proofs">proofs</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<h1>Proofs</h1>
|
||||
<div class="content">
|
||||
<form id="form-proofs" method="post">
|
||||
<h3>Public Key (1: plaintext)</h3>
|
||||
<textarea name="publicKey" id="publicKey"></textarea>
|
||||
<h3>Public Key (2: web key directory)</h3>
|
||||
<input type="text" name="wkd" id="wkd" placeholder="name@domain.com">
|
||||
<h3>Public Key (3: HKP server)</h3>
|
||||
<input type="text" name="hkp_server" id="hkp_server" placeholder="https://keys.openpgp.org/">
|
||||
<input type="text" name="hkp_input" id="hkp_input" placeholder="Email / key id / fingerprint">
|
||||
<h3>Result</h3>
|
||||
<p id="result">Click on the button below.</p>
|
||||
<p id="resultContent"></p>
|
||||
<input type="submit" class="bigBtn" name="submit" value="VERIFY PROOFS" value="">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
<a href="/">Keyoxide</a> makes basic cryptography operations accessible to regular humans.
|
||||
<br>
|
||||
Made by <a href="https://yarmo.eu">Yarmo Mackenbach</a>.
|
||||
<br>
|
||||
Code hosted on <a href="https://codeberg.org/yarmo/keyoxide">Codeberg</a> (<a href="https://drone.private.foss.best/yarmo/keyoxide/">drone CI/CD</a>).
|
||||
<br>
|
||||
Uses <a href="https://github.com/openpgpjs/openpgpjs">openpgp.js</a> (version <a href="https://github.com/openpgpjs/openpgpjs/releases/tag/v4.10.4">4.10.4</a>).
|
||||
<br>
|
||||
Privacy-friendly public usage stats by <a href="https://plausible.io/keyoxide.org">Plausible.io</a>.
|
||||
<br>
|
||||
Because SO2 + 2NaOH → Na2SO3 + H2O
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
<script src="openpgp.min.js"></script>
|
||||
<script type="text/javascript" src="scripts.js" charset="utf-8"></script>
|
||||
</html>
|
199
scripts.js
199
scripts.js
|
@ -132,6 +132,177 @@ async function encryptMessage(opts) {
|
|||
elEnc.value = encrypted.data;
|
||||
};
|
||||
|
||||
async function verifyProofs(opts) {
|
||||
// Init
|
||||
const elRes = document.body.querySelector("#result");
|
||||
let keyData, feedback = "", message, encrypted;
|
||||
|
||||
// Reset feedback
|
||||
elRes.innerHTML = "";
|
||||
|
||||
try {
|
||||
// Get key data
|
||||
keyData = await fetchKeys(opts);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
elRes.innerHTML = e;
|
||||
elRes.classList.remove('green');
|
||||
elRes.classList.add('red');
|
||||
return;
|
||||
}
|
||||
|
||||
let notation, isVerified, verifications = [];
|
||||
for (var i = 0; i < keyData.notations.length; i++) {
|
||||
notation = keyData.notations[i];
|
||||
if (!(notation[0] == "proof@keyoxide.org" || notation[0] == "proof@metacode.biz")) { continue; }
|
||||
verifications.push(await verifyProof(notation[1], keyData.fingerprint));
|
||||
}
|
||||
|
||||
// Generate feedback
|
||||
for (var i = 0; i < verifications.length; i++) {
|
||||
feedback += `${verifications[i].type}: <a href="${verifications[i].url}">${verifications[i].display}</a>: ${verifications[i].isVerified}<br>`;
|
||||
}
|
||||
|
||||
// Display feedback
|
||||
elRes.innerHTML = feedback;
|
||||
};
|
||||
|
||||
async function verifyProof(url, fingerprint) {
|
||||
// Init
|
||||
let reVerify, urlFetch, output = {url: url, type: null, isVerified: false, display: null};
|
||||
|
||||
// DNS
|
||||
if (/^dns:/.test(url)) {
|
||||
output.type = "dns";
|
||||
let domain = url.replace(/dns:/, '').replace(/\?type=TXT/, '');
|
||||
urlFetch = `https://dns.google.com/resolve?name=${domain}&type=TXT`;
|
||||
output.display = domain;
|
||||
|
||||
try {
|
||||
response = await fetch(urlFetch, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
credentials: 'omit'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Response failed: ' + response.status);
|
||||
}
|
||||
json = await response.json();
|
||||
reVerify = new RegExp(`openpgp4fpr:${fingerprint}`);
|
||||
json.Answer.forEach((item, i) => {
|
||||
if (reVerify.test(item.data)) {
|
||||
output.isVerified = true;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
} finally {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
// HN
|
||||
if (/^https:\/\/news.ycombinator.com/.test(url)) {
|
||||
output.type = "hn";
|
||||
try {
|
||||
response = await fetch(urlFetch, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
credentials: 'omit'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Response failed: ' + response.status);
|
||||
}
|
||||
json = await response.json();
|
||||
reVerify = new RegExp(`openpgp4fpr:${fingerprint}`);
|
||||
json.Answer.forEach((item, i) => {
|
||||
if (reVerify.test(item.data)) {
|
||||
output.isVerified = true;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
} finally {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
// Reddit
|
||||
if (/^https:\/\/www.reddit.com\/user/.test(url)) {
|
||||
output.type = "reddit";
|
||||
try {
|
||||
response = await fetch(urlFetch, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
credentials: 'omit'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Response failed: ' + response.status);
|
||||
}
|
||||
json = await response.json();
|
||||
reVerify = new RegExp(`openpgp4fpr:${fingerprint}`);
|
||||
json.Answer.forEach((item, i) => {
|
||||
if (reVerify.test(item.data)) {
|
||||
output.isVerified = true;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
} finally {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
// Github
|
||||
if (/^https:\/\/gist.github.com/.test(url)) {
|
||||
output.type = "github";
|
||||
try {
|
||||
response = await fetch(urlFetch, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
credentials: 'omit'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Response failed: ' + response.status);
|
||||
}
|
||||
json = await response.json();
|
||||
reVerify = new RegExp(`openpgp4fpr:${fingerprint}`);
|
||||
json.Answer.forEach((item, i) => {
|
||||
if (reVerify.test(item.data)) {
|
||||
output.isVerified = true;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
} finally {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
// Catchall
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
credentials: 'omit'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Response failed: ' + response.status);
|
||||
}
|
||||
json = await response.json();
|
||||
if ('attachment' in json) {
|
||||
// Potentially Mastodon
|
||||
json.attachment.forEach((item, i) => {
|
||||
if (item.value === fingerprint) {
|
||||
output.type = "mastodon";
|
||||
output.display = json.url;
|
||||
output.isVerified = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchKeys(opts) {
|
||||
// Init
|
||||
let lookupOpts, wkd, hkd, sig, lastPrimarySig;
|
||||
|
@ -215,6 +386,7 @@ async function fetchKeys(opts) {
|
|||
// General purpose
|
||||
let elFormVerify = document.body.querySelector("#form-verify"),
|
||||
elFormEncrypt = document.body.querySelector("#form-encrypt");
|
||||
elFormProofs = document.body.querySelector("#form-proofs");
|
||||
|
||||
if (elFormVerify) {
|
||||
elFormVerify.onsubmit = function (evt) {
|
||||
|
@ -275,3 +447,30 @@ if (elFormEncrypt) {
|
|||
encryptMessage(opts);
|
||||
};
|
||||
}
|
||||
|
||||
if (elFormProofs) {
|
||||
elFormProofs.onsubmit = function (evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
let opts = {
|
||||
mode: null,
|
||||
input: null,
|
||||
server: null,
|
||||
};
|
||||
|
||||
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";
|
||||
} else {
|
||||
opts.mode = null;
|
||||
}
|
||||
verifyProofs(opts);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<nav>
|
||||
<a href="/verify">verify</a>
|
||||
<a href="/encrypt">encrypt</a>
|
||||
<a href="/proofs">proofs</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
Loading…
Reference in a new issue