Compare commits
5 Commits
ab676217cd
...
5dba3f8675
Author | SHA1 | Date | |
---|---|---|---|
5dba3f8675 | |||
0e0ac98c84 | |||
7ed8cc7e3a | |||
6972790053 | |||
3caf1b72ba |
8
aes.html
8
aes.html
@ -3,14 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<title>encryptme: Simple AES encryption/decryption</title>
|
||||||
<meta name="description" content="Easy to use and simple online tool for AES encryption and decryption.
|
<meta name="description" content="Easy to use and simple online tool for AES encryption and decryption.
|
||||||
Advanced settings allow control over the IV, AES mode, and PBKDF2 parameters.">
|
Advanced settings allow control over the IV, AES mode, and PBKDF2 parameters.">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
<title>encryptme: Simple AES encryption/decryption</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>encryptme</h1>
|
<script src="scripts/header-template.js"></script>
|
||||||
<h1>AES</h1>
|
<h1>AES</h1>
|
||||||
<script src="scripts/interface.js"></script>
|
<script src="scripts/interface.js"></script>
|
||||||
<script src="scripts/aes.js"></script>
|
<script src="scripts/aes.js"></script>
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="Easy to use and simple online tools for encryption and decryption.">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
<title>encryptme</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<title>encryptme</title>
|
||||||
|
<meta name="description" content="Easy to use and simple online tools for encryption and decryption.">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>encryptme</h1>
|
<script src="scripts/header-template.js"></script>
|
||||||
<h2>Tools</h2>
|
<h2>Tools</h2>
|
||||||
<h3>Encryption/decryption</h3>
|
<h3>Encryption/decryption</h3>
|
||||||
<a href="aes.html">AES</a>
|
<a href="aes.html">AES</a>
|
||||||
|
127
scripts/aes.js
127
scripts/aes.js
@ -61,6 +61,20 @@ let encManualIV = encForm.createCheckBox({
|
|||||||
label: "Use fixed IV instead of random",
|
label: "Use fixed IV instead of random",
|
||||||
advanced: true
|
advanced: true
|
||||||
});
|
});
|
||||||
|
let encMode = encForm.createDropDown({
|
||||||
|
label: "AES mode",
|
||||||
|
advanced: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "AES-GCM (Galois/Counter Mode)",
|
||||||
|
value: "AES-GCM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AES-CBC (Cipher Block Chaining)",
|
||||||
|
value: "AES-CBC"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
let encButton = encForm.createButton({label: "Encrypt"});
|
let encButton = encForm.createButton({label: "Encrypt"});
|
||||||
let encOut = encForm.createOutput({
|
let encOut = encForm.createOutput({
|
||||||
label: "Output",
|
label: "Output",
|
||||||
@ -107,7 +121,7 @@ function getKeyMaterial(password) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKey(keyMaterial, salt, pbkdf2Iters) {
|
function getKey(keyMaterial, salt, pbkdf2Iters, encMode) {
|
||||||
return window.crypto.subtle.deriveKey(
|
return window.crypto.subtle.deriveKey(
|
||||||
{
|
{
|
||||||
"name": "PBKDF2",
|
"name": "PBKDF2",
|
||||||
@ -117,7 +131,7 @@ function getKey(keyMaterial, salt, pbkdf2Iters) {
|
|||||||
},
|
},
|
||||||
keyMaterial,
|
keyMaterial,
|
||||||
{
|
{
|
||||||
"name": "AES-GCM",
|
"name": encMode,
|
||||||
"length": 256
|
"length": 256
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
@ -125,6 +139,27 @@ function getKey(keyMaterial, salt, pbkdf2Iters) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function aesGcmEnc(key, iv, msgEncoded) {
|
||||||
|
return window.crypto.subtle.encrypt(
|
||||||
|
{
|
||||||
|
"name": "AES-GCM",
|
||||||
|
"iv": iv
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
msgEncoded
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async function aesCbcEnc(key, iv, msgEncoded) {
|
||||||
|
return window.crypto.subtle.encrypt(
|
||||||
|
{
|
||||||
|
"name": "AES-CBC",
|
||||||
|
"iv": iv
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
msgEncoded
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
encButton.handle.addEventListener("click", async function() {
|
encButton.handle.addEventListener("click", async function() {
|
||||||
let keyMaterial = await getKeyMaterial(encPass.value);
|
let keyMaterial = await getKeyMaterial(encPass.value);
|
||||||
let key;
|
let key;
|
||||||
@ -140,7 +175,7 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
key = await window.crypto.subtle.importKey(
|
key = await window.crypto.subtle.importKey(
|
||||||
"raw",
|
"raw",
|
||||||
encKey.value,
|
encKey.value,
|
||||||
{"name": "AES-GCM"},
|
{"name": encMode.value},
|
||||||
true,
|
true,
|
||||||
["encrypt", "decrypt"]
|
["encrypt", "decrypt"]
|
||||||
);
|
);
|
||||||
@ -152,7 +187,7 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
encSalt.value = salt;
|
encSalt.value = salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = await getKey(keyMaterial, salt, pbkdf2Iters);
|
key = await getKey(keyMaterial, salt, pbkdf2Iters, encMode.value);
|
||||||
encKey.value = await window.crypto.subtle.exportKey("raw", key);
|
encKey.value = await window.crypto.subtle.exportKey("raw", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,14 +202,19 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
let enc = new TextEncoder();
|
let enc = new TextEncoder();
|
||||||
let msgEncoded = enc.encode(encMsg.value);
|
let msgEncoded = enc.encode(encMsg.value);
|
||||||
|
|
||||||
let ciphertext = await window.crypto.subtle.encrypt(
|
let ciphertext;
|
||||||
{
|
switch (encMode.value) {
|
||||||
"name": "AES-GCM",
|
case "AES-GCM":
|
||||||
"iv": iv
|
ciphertext = await aesGcmEnc(key, iv, msgEncoded);
|
||||||
},
|
break;
|
||||||
key,
|
case "AES-CBC":
|
||||||
msgEncoded
|
ciphertext = await aesCbcEnc(key, iv, msgEncoded);
|
||||||
);
|
break;
|
||||||
|
default:
|
||||||
|
let e = Error(`Mode '${encMode.value}' is not implemented.`);
|
||||||
|
encMode.handleError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
encOutRaw.value = ciphertext;
|
encOutRaw.value = ciphertext;
|
||||||
|
|
||||||
@ -182,26 +222,49 @@ encButton.handle.addEventListener("click", async function() {
|
|||||||
"ciphertext": bufToB64(ciphertext),
|
"ciphertext": bufToB64(ciphertext),
|
||||||
"salt": bufToB64(salt),
|
"salt": bufToB64(salt),
|
||||||
"iv": bufToB64(iv),
|
"iv": bufToB64(iv),
|
||||||
"pbkdf2Iters": pbkdf2Iters
|
"encMode": encMode.value,
|
||||||
|
"pbkdf2Iters": pbkdf2Iters,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function aesGcmDec(key, iv, ciphertext) {
|
||||||
|
return window.crypto.subtle.decrypt(
|
||||||
|
{
|
||||||
|
"name": "AES-GCM",
|
||||||
|
"iv": iv
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
ciphertext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async function aesCbcDec(key, iv, ciphertext) {
|
||||||
|
return window.crypto.subtle.decrypt(
|
||||||
|
{
|
||||||
|
"name": "AES-CBC",
|
||||||
|
"iv": iv
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
ciphertext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
decButton.handle.addEventListener("click", async function() {
|
decButton.handle.addEventListener("click", async function() {
|
||||||
let msgEncoded = decMsg.value;
|
let msgEncoded = decMsg.value;
|
||||||
|
|
||||||
let ciphertext, iv, salt, pbkdf2Iters;
|
let ciphertext, iv, salt, encMode, pbkdf2Iters;
|
||||||
try {
|
try {
|
||||||
ciphertext = new b64ToBuf(msgEncoded.ciphertext);
|
ciphertext = new b64ToBuf(msgEncoded.ciphertext);
|
||||||
iv = new Uint8Array(b64ToBuf(msgEncoded.iv));
|
iv = new Uint8Array(b64ToBuf(msgEncoded.iv));
|
||||||
salt = new Uint8Array(b64ToBuf(msgEncoded.salt));
|
salt = new Uint8Array(b64ToBuf(msgEncoded.salt));
|
||||||
|
encMode = msgEncoded.encMode;
|
||||||
pbkdf2Iters = msgEncoded.pbkdf2Iters;
|
pbkdf2Iters = msgEncoded.pbkdf2Iters;
|
||||||
if (pbkdf2Iters < 1 || pbkdf2Iters%1 !== 0) {
|
if (pbkdf2Iters < 1 || pbkdf2Iters%1 !== 0) {
|
||||||
decMsg.alertBox("alert-error", "Invalid PBKDF2 iters setting.");
|
throw Error(`Invalid PBKDF2 iterations setting: ${pbkdf2Iters}`);
|
||||||
} else if (pbkdf2Iters > 1000000) {
|
} else if (pbkdf2Iters > 1000000) {
|
||||||
decMsg.alertBox("alert-info", `PBKDF2 is using ${pbkdf2Iters} iterations: this might take a long time...`);
|
decMsg.alertBox("alert-info", `PBKDF2 is using ${pbkdf2Iters} iterations: this might take a long time...`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
decMsg.alertBox("alert-error", "Invalid encrypted payload.");
|
decMsg.handleError(e, "Invalid encrypted payload.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ciphertext === undefined
|
if (ciphertext === undefined
|
||||||
@ -215,30 +278,40 @@ decButton.handle.addEventListener("click", async function() {
|
|||||||
let keyMaterial = await getKeyMaterial(decPass.value);
|
let keyMaterial = await getKeyMaterial(decPass.value);
|
||||||
let key;
|
let key;
|
||||||
if (decManualKey.value) {
|
if (decManualKey.value) {
|
||||||
|
try {
|
||||||
key = await window.crypto.subtle.importKey(
|
key = await window.crypto.subtle.importKey(
|
||||||
"raw",
|
"raw",
|
||||||
decKey.value,
|
decKey.value,
|
||||||
{"name": "AES-GCM"},
|
{"name": encMode},
|
||||||
true,
|
true,
|
||||||
["encrypt", "decrypt"]
|
["encrypt", "decrypt"]
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
decMsg.handleError(e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
key = await getKey(keyMaterial, salt, pbkdf2Iters);
|
key = await getKey(keyMaterial, salt, pbkdf2Iters, encMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let plaintext;
|
let plaintext;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
plaintext = await window.crypto.subtle.decrypt(
|
switch (encMode) {
|
||||||
{
|
case "AES-GCM":
|
||||||
"name": "AES-GCM",
|
plaintext = await aesGcmDec(key, iv, ciphertext);
|
||||||
"iv": iv
|
break;
|
||||||
},
|
case "AES-CBC":
|
||||||
key,
|
plaintext = await aesCbcDec(key, iv, ciphertext);
|
||||||
ciphertext
|
break;
|
||||||
);
|
default:
|
||||||
|
throw Error(`Mode '${encMode.value}' is not implemented.`);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
decPass.alertBox("alert-error", "Decryption error: incorrect password?");
|
if (e.message !== "" && e.message !== undefined) {
|
||||||
|
decMsg.handleError(e, "Error during decryption.");
|
||||||
|
} else {
|
||||||
|
decMsg.handleError(Error("Could not decrypt; is your password/key correct?"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dec = new TextDecoder();
|
let dec = new TextDecoder();
|
||||||
|
8
scripts/header-template.js
Normal file
8
scripts/header-template.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
header = document.createElement("div");
|
||||||
|
header.classList.add("page-header");
|
||||||
|
|
||||||
|
header.innerHTML = `
|
||||||
|
<a href="index.html"><h1>encryptme</h1></a>
|
||||||
|
`
|
||||||
|
|
||||||
|
document.body.appendChild(header);
|
@ -194,6 +194,36 @@ class Form extends InterfaceElement {
|
|||||||
return this.appendElement(new FormElement(params));
|
return this.appendElement(new FormElement(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createDropDown(params) {
|
||||||
|
// example for params.options:
|
||||||
|
/*
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: "volvo"
|
||||||
|
name: "Volvo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "benz"
|
||||||
|
name: "Mercedes Benz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
params.fragment = new DocumentFragment();
|
||||||
|
params.tag = document.createElement("select");
|
||||||
|
params.labelTag = document.createElement("label");
|
||||||
|
params.labelTag.appendChild(document.createTextNode(params.label));
|
||||||
|
params.fragment.appendChild(params.labelTag);
|
||||||
|
params.fragment.appendChild(params.tag);
|
||||||
|
dataTypeSupports(params, ["category"]);
|
||||||
|
for (const option of params.options) {
|
||||||
|
let optTag = document.createElement("option");
|
||||||
|
optTag.value = option.value;
|
||||||
|
optTag.appendChild(document.createTextNode(option.name));
|
||||||
|
params.tag.appendChild(optTag);
|
||||||
|
}
|
||||||
|
return this.appendElement(new FormElement(params));
|
||||||
|
}
|
||||||
|
|
||||||
createTextArea(params) {
|
createTextArea(params) {
|
||||||
params.tag = document.createElement("textarea");
|
params.tag = document.createElement("textarea");
|
||||||
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
dataTypeSupports(params, ["plaintext", "b64", "json-b64"]);
|
||||||
@ -316,6 +346,8 @@ class FormElement extends InterfaceElement {
|
|||||||
}
|
}
|
||||||
case "bool":
|
case "bool":
|
||||||
return this.handle.checked;
|
return this.handle.checked;
|
||||||
|
case "category":
|
||||||
|
return this.handle.value;
|
||||||
case "none":
|
case "none":
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -335,6 +367,9 @@ class FormElement extends InterfaceElement {
|
|||||||
case "bool":
|
case "bool":
|
||||||
this.handle.checked = x;
|
this.handle.checked = x;
|
||||||
break;
|
break;
|
||||||
|
case "category":
|
||||||
|
this.handle.value = x;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,6 +413,13 @@ class FormElement extends InterfaceElement {
|
|||||||
this.handle.after(box);
|
this.handle.after(box);
|
||||||
this.alerts.push(box);
|
this.alerts.push(box);
|
||||||
}
|
}
|
||||||
|
handleError(e, extraInfo="") {
|
||||||
|
if (extraInfo !== "") {
|
||||||
|
extraInfo = ` (${extraInfo})`;
|
||||||
|
}
|
||||||
|
this.alertBox("alert-error", e.message + extraInfo);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
clearAlerts() {
|
clearAlerts() {
|
||||||
for (const box of this.alerts) {
|
for (const box of this.alerts) {
|
||||||
box.remove();
|
box.remove();
|
||||||
|
@ -127,6 +127,11 @@ button:active {
|
|||||||
background: #0077ff44;
|
background: #0077ff44;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-header a {
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body {
|
body {
|
||||||
color: #c9d1d9;
|
color: #c9d1d9;
|
||||||
@ -149,6 +154,10 @@ button:active {
|
|||||||
background: #1d2127;
|
background: #1d2127;
|
||||||
color: #c9d1d9;
|
color: #c9d1d9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-header a {
|
||||||
|
color: #c9d1d9;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[hidden] {
|
[hidden] {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user