forked from Mirrors/keyoxide-web
Initial commit
This commit is contained in:
commit
d0bb087ba2
9 changed files with 550 additions and 0 deletions
15
.drone.yml
Normal file
15
.drone.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
kind: pipeline
|
||||||
|
type: exec
|
||||||
|
name: deploy
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: deploy to server
|
||||||
|
environment:
|
||||||
|
DEPLOY_COMMAND:
|
||||||
|
from_secret: deploy_command
|
||||||
|
commands:
|
||||||
|
- $${DEPLOY_COMMAND}
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- main
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
MIT License Copyright (c) 2020 Yarmo Mackenbach
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Keyoxide
|
||||||
|
|
||||||
|
[Keyoxide](https://keyoxide.org) is a FOSS solution to make basic cryptography operations accessible to regular humans.
|
||||||
|
|
||||||
|
[![Build Status](https://drone.private.foss.best/api/badges/yarmo/keyoxide/status.svg)](https://drone.private.foss.best/yarmo/keyoxide)
|
57
encrypt.html
Normal file
57
encrypt.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Encrypt - 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>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>Encrypt</h1>
|
||||||
|
<div class="content">
|
||||||
|
<form id="form-encrypt" method="post">
|
||||||
|
<h3>Message</h3>
|
||||||
|
<textarea name="message" id="message"></textarea>
|
||||||
|
<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 address / key id / fingerprint">
|
||||||
|
<h3>Result</h3>
|
||||||
|
<textarea name="messageEncrypted" id="messageEncrypted" readonly></textarea>
|
||||||
|
<p id="result"></p>
|
||||||
|
<input type="submit" name="submit" value="ENCRYPT MESSAGE">
|
||||||
|
</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>.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script src="openpgp.min.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts.js" charset="utf-8"></script>
|
||||||
|
</html>
|
50
index.html
Normal file
50
index.html
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>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>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>Keyoxide</h1>
|
||||||
|
<div class="content">
|
||||||
|
<p>Because SO2 + 2NaOH → Na2SO3 + H2O</p>
|
||||||
|
<h2>Basic operations</h2>
|
||||||
|
<p>
|
||||||
|
<a href="/verify">verify</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="/encrypt">encrypt</a>
|
||||||
|
</p>
|
||||||
|
</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>.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script src="openpgp.min.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts.js" charset="utf-8"></script>
|
||||||
|
</html>
|
2
openpgp.min.js
vendored
Normal file
2
openpgp.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
277
scripts.js
Normal file
277
scripts.js
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
async function verifySignature(opts) {
|
||||||
|
const elRes = document.body.querySelector("#result");
|
||||||
|
const elResContent = document.body.querySelector("#resultContent");
|
||||||
|
let feedback, signature, verified, publicKey, fp, lookupOpts, wkd, hkp, sig, userId, keyId, sigContent;
|
||||||
|
|
||||||
|
elRes.innerHTML = "";
|
||||||
|
elResContent.innerHTML = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (opts.mode) {
|
||||||
|
case "plaintext":
|
||||||
|
publicKey = (await openpgp.key.readArmored(opts.input)).keys;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "wkd":
|
||||||
|
wkd = new openpgp.WKD();
|
||||||
|
lookupOpts = {
|
||||||
|
email: opts.input
|
||||||
|
};
|
||||||
|
publicKey = (await wkd.lookup(lookupOpts)).keys;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "hkp":
|
||||||
|
if (!opts.server) {opts.server = "https://keys.openpgp.org/"};
|
||||||
|
hkp = new openpgp.HKP(opts.server);
|
||||||
|
lookupOpts = {
|
||||||
|
query: opts.input
|
||||||
|
};
|
||||||
|
publicKey = await hkp.lookup(lookupOpts);
|
||||||
|
publicKey = (await openpgp.key.readArmored(publicKey)).keys;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sig = (await openpgp.signature.readArmored(opts.signature));
|
||||||
|
if ('compressed' in sig.packets[0]) {
|
||||||
|
sig = sig.packets[0];
|
||||||
|
sigContent = (await openpgp.stream.readToEnd(await sig.packets[1].getText()));
|
||||||
|
};
|
||||||
|
keyId = (await sig.packets[0].issuerKeyId.toHex());
|
||||||
|
userId = sig.packets[0].signersUserId;
|
||||||
|
|
||||||
|
if (!keyId && !userId) {
|
||||||
|
elRes.innerHTML = "The signature does not contain a valid keyId or userId.";
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts.server) {opts.server = "https://keys.openpgp.org/"};
|
||||||
|
hkp = new openpgp.HKP(opts.server);
|
||||||
|
lookupOpts = {
|
||||||
|
query: userId ? userId : keyId
|
||||||
|
};
|
||||||
|
publicKey = await hkp.lookup(lookupOpts);
|
||||||
|
publicKey = (await openpgp.key.readArmored(publicKey)).keys;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.signature == null) {
|
||||||
|
elRes.innerHTML = "No signature was provided.";
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (signature == null) {throw(readError)};
|
||||||
|
|
||||||
|
fp = publicKey[0].getFingerprint();
|
||||||
|
verified = await openpgp.verify({
|
||||||
|
message: signature,
|
||||||
|
publicKeys: publicKey
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
elRes.innerHTML = e;
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
feedback = '';
|
||||||
|
const valid = verified.signatures[0];
|
||||||
|
|
||||||
|
if (sigContent) {
|
||||||
|
elResContent.innerHTML = "<strong>Signature content:</strong><br><span style=\"white-space: pre-line\">"+sigContent+"</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
if (valid) {
|
||||||
|
feedback += "The message was signed by the userId extracted from the signature.<br>";
|
||||||
|
feedback += 'UserId: '+userId+'<br>';
|
||||||
|
feedback += "Fingerprint: "+fp+"<br>";
|
||||||
|
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>";
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
}
|
||||||
|
} else if (keyId) {
|
||||||
|
if (valid) {
|
||||||
|
feedback += "The message was signed by the keyId extracted from the signature.<br>";
|
||||||
|
feedback += 'KeyID: '+keyId+'<br>';
|
||||||
|
feedback += "Fingerprint: "+fp+"<br><br>";
|
||||||
|
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>";
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (valid) {
|
||||||
|
feedback += "The message was signed by the provided key ("+opts.mode+").<br>";
|
||||||
|
feedback += "Fingerprint: "+fp+"<br>";
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elRes.innerHTML = feedback;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function encryptMessage(opts) {
|
||||||
|
const elEnc = document.body.querySelector("#messageEncrypted");
|
||||||
|
const elRes = document.body.querySelector("#result");
|
||||||
|
let feedback, message, verified, publicKey, fp, lookupOpts, wkd, hkp, sig, userId, keyId, sigContent;
|
||||||
|
|
||||||
|
elRes.innerHTML = "";
|
||||||
|
elEnc.value = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (opts.mode) {
|
||||||
|
case "plaintext":
|
||||||
|
publicKey = (await openpgp.key.readArmored(opts.input)).keys;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "wkd":
|
||||||
|
wkd = new openpgp.WKD();
|
||||||
|
lookupOpts = {
|
||||||
|
email: opts.input
|
||||||
|
};
|
||||||
|
publicKey = (await wkd.lookup(lookupOpts)).keys;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "hkp":
|
||||||
|
if (!opts.server) {opts.server = "https://keys.openpgp.org/"};
|
||||||
|
hkp = new openpgp.HKP(opts.server);
|
||||||
|
lookupOpts = {
|
||||||
|
query: opts.input
|
||||||
|
};
|
||||||
|
publicKey = await hkp.lookup(lookupOpts);
|
||||||
|
publicKey = (await openpgp.key.readArmored(publicKey)).keys;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sig = (await openpgp.signature.readArmored(opts.message));
|
||||||
|
if ('compressed' in sig.packets[0]) {
|
||||||
|
sig = sig.packets[0];
|
||||||
|
sigContent = (await openpgp.stream.readToEnd(await sig.packets[1].getText()));
|
||||||
|
};
|
||||||
|
keyId = (await sig.packets[0].issuerKeyId.toHex());
|
||||||
|
userId = sig.packets[0].signersUserId;
|
||||||
|
|
||||||
|
if (!keyId && !userId) {
|
||||||
|
elRes.innerHTML = "The signature does not contain a valid keyId or userId.";
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts.server) {opts.server = "https://keys.openpgp.org/"};
|
||||||
|
hkp = new openpgp.HKP(opts.server);
|
||||||
|
lookupOpts = {
|
||||||
|
query: userId ? userId : keyId
|
||||||
|
};
|
||||||
|
publicKey = await hkp.lookup(lookupOpts);
|
||||||
|
publicKey = (await openpgp.key.readArmored(publicKey)).keys;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.message == null) {
|
||||||
|
elRes.innerHTML = "No message was provided.";
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted = await openpgp.encrypt({
|
||||||
|
message: openpgp.message.fromText(opts.message),
|
||||||
|
publicKeys: publicKey
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
elRes.innerHTML = e;
|
||||||
|
elRes.classList.remove('green');
|
||||||
|
elRes.classList.add('red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
elEnc.value = encrypted.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
encryptMessage(opts);
|
||||||
|
};
|
||||||
|
}
|
68
styles.css
Normal file
68
styles.css
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-color: #eee;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
padding: 16px;
|
||||||
|
margin: 0 0 48px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
color: #777;
|
||||||
|
margin: 64px 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 640px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 16px 32px 32px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px 32px;
|
||||||
|
background-color: #9dd3f0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #3f9acc;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 128px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
input[type="radio"] {
|
||||||
|
vertical-align: sub;
|
||||||
|
}
|
||||||
|
input[type="submit"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: #c3eaff;
|
||||||
|
border: 1px solid #3f9acc;
|
||||||
|
border-radius: 3px;
|
||||||
|
border-bottom-width: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input[type="submit"]:hover {
|
||||||
|
background-color: #9dd3f0;
|
||||||
|
}
|
||||||
|
.green {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
}
|
57
verify.html
Normal file
57
verify.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Verify - 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>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>Verify</h1>
|
||||||
|
<div class="content">
|
||||||
|
<form id="form-verify" method="post">
|
||||||
|
<h3>Signature</h3>
|
||||||
|
<textarea name="signature" id="signature"></textarea>
|
||||||
|
<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 address / key id / fingerprint">
|
||||||
|
<h3>Result</h3>
|
||||||
|
<p id="result">Click on the button below.</p>
|
||||||
|
<p id="resultContent"></p>
|
||||||
|
<input type="submit" name="submit" value="VERIFY SIGNATURE" 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>.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script src="openpgp.min.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts.js" charset="utf-8"></script>
|
||||||
|
</html>
|
Loading…
Reference in a new issue