Last active
December 20, 2021 13:17
-
-
Save FND/06f761d11425a220cc7ab8188e102eaf to your computer and use it in GitHub Desktop.
encryption/decryption
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Encryption/Decryption</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
*, | |
*::before, | |
*::after { | |
box-sizing: border-box; | |
} | |
body { | |
max-width: 60ch; | |
margin: 1rem auto; | |
font-family: sans-serif; | |
} | |
form > * { | |
margin-top: 0; | |
margin-bottom: 0; | |
} | |
form > * + * { | |
margin-top: 0.5rem; | |
} | |
textarea { | |
display: block; | |
width: 100%; | |
height: 5rem; | |
} | |
label, | |
output { | |
display: block; | |
} | |
output[name=encrypted] { | |
word-break: break-all; | |
} | |
output[name=decrypted] { | |
white-space: pre; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Encryption/Decryption</h1> | |
<p> | |
adapted from vjeux's | |
<a href="https://blog.excalidraw.com/end-to-end-encryption/">Excalidraw write-up</a> | |
</p> | |
<form> | |
<textarea>lörem ipsüm dolor ßit ämet</textarea> | |
<button>check</button> | |
<label> | |
<b>password:</b> | |
<input value="s33kr1t"> | |
</label> | |
<label> | |
<b>encrypted:</b> | |
<output name="encrypted"></output> | |
</label> | |
<label> | |
<b>decrypted:</b> | |
<output name="decrypted"></output> | |
</label> | |
</form> | |
<script> | |
(function() { | |
let SCRYPT = window.crypto.subtle; | |
let ALGO = "AES-GCM"; | |
let SEP = "|"; | |
let SALT = str2bytes("a67190bb-e9f4-4c3f-b690-392414a46d3b").buffer; | |
document.querySelector("form").addEventListener("submit", onSubmit); | |
async function onSubmit(ev) { | |
ev.preventDefault(); | |
let content = this.querySelector("textarea").value; | |
let password = this.querySelector("input").value; | |
let encrypted = await encrypt(content, password); | |
Object.entries({ | |
encrypted: encrypted, | |
decrypted: await decrypt(encrypted, password) | |
}).forEach(([name, value]) => { | |
this.querySelector(`[name=${name}]`).textContent = value; | |
}); | |
} | |
async function encrypt(content, password) { | |
let iv = window.crypto.getRandomValues(new Uint8Array(16)); | |
let encrypted = await SCRYPT.encrypt({ name: ALGO, iv }, | |
await deriveKey(password), str2bytes(content)); | |
return [iv, new Uint8Array(encrypted)]. // XXX: conversion valid? | |
map(block => btoa(block.join(","))). | |
join(SEP); | |
} | |
async function decrypt(content, password) { | |
let [iv, encrypted] = content.split(SEP). | |
map(block => Uint8Array.from(atob(block).split(","), str2int)); | |
let decrypted = await SCRYPT.decrypt({ name: ALGO, iv }, | |
await deriveKey(password), encrypted); | |
return new window.TextDecoder().decode(new Uint8Array(decrypted)); | |
} | |
async function deriveKey(password) { | |
let secret = await SCRYPT.importKey("raw", str2bytes(password), "PBKDF2", | |
false, ["deriveBits", "deriveKey"]); | |
return await SCRYPT.deriveKey({ | |
name: "PBKDF2", | |
salt: SALT, | |
iterations: 100000, | |
hash: "SHA-256" | |
}, secret, { name: ALGO, length: 256 }, true, ["encrypt", "decrypt"]); | |
} | |
function str2bytes(v) { | |
return new TextEncoder().encode(v); | |
} | |
function str2int(v) { | |
return parseInt(v, 10); | |
} | |
}()); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment