Skip to content

Instantly share code, notes, and snippets.

@cat-in-136
Created August 16, 2017 07:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cat-in-136/de9344f7194f0b1856756b701ea10b1c to your computer and use it in GitHub Desktop.
Save cat-in-136/de9344f7194f0b1856756b701ea10b1c to your computer and use it in GitHub Desktop.
window.crypto test inspired by Firefox Send
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>window.crypto test</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" type="text/css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.min.css" rel="stylesheet" type="text/css">
<script type="application/javascript" defer="defer" src="https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.min.js"></script>
</head>
<body>
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header
mdl-layout--fixed-tabs">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<!-- Title -->
<span class="mdl-layout-title">window.crypto test</span>
</div>
<!-- Tabs -->
<div class="mdl-layout__tab-bar mdl-js-ripple-effect">
<a href="#encrypt-tab" class="mdl-layout__tab is-active">Encrypt</a>
<a href="#decrypt-tab" class="mdl-layout__tab">Decrypt</a>
</div>
</header>
<main class="mdl-layout__content">
<section class="mdl-layout__tab-panel is-active" id="encrypt-tab">
<div class="mdl-layout__container">
<div class="mdl-grid">
<div class="mdl-button mdl-button--raised mdl-button--colored">
<label for="file-upload">Encrypt File ...</label>
<input id="file-upload" name="fileUpload" type="file" style="display:none;">
</div>
</div>
<output id="file-encrypted" class="mdl-grid">
</output>
</div>
</section>
<section class="mdl-layout__tab-panel" id="decrypt-tab">
<div class="mdl-layout__container">
<div class="mdl-grid">
<div class="mdl-button mdl-button--raised mdl-button--colored">
<label for="file-download">Decrypt File ...</label>
<input id="file-download" name="fileDownload" type="file" style="display:none;">
</div>
</div>
<output id="file-decrypted" class="mdl-grid">
</output>
</div>
</section>
</main>
</div>
<script type="application/javascript">
function arrayToHex(iv) {
let hexStr = '';
// eslint-disable-next-line prefer-const
for (let i in iv) {
if (iv[i] < 16) {
hexStr += '0' + iv[i].toString(16);
} else {
hexStr += iv[i].toString(16);
}
}
return hexStr;
}
function hexToArray(str) {
const iv = new Uint8Array(str.length / 2);
for (let i = 0; i < str.length; i += 2) {
iv[i / 2] = parseInt(str.charAt(i) + str.charAt(i + 1), 16);
}
return iv;
}
document.getElementById("file-upload").addEventListener("change", function (event) {
"use strict";
event.preventDefault();
const file = event.target.files[0];
const stat = document.createElement("div");
stat.setAttribute("class", "mdl-card mdl-shadow--2dp mdl-cell");
stat.innerHTML = `
<div class="mdl-card__title"></div>
<div class="mdl-card__supporting-text"></div>
<div class="mdl-card__actions"></div>
<div class="mdl-card__menu">
<button class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">clear</i>
</button>
</div>
`;
stat.querySelector(".mdl-card__menu button").addEventListener("click", () => {
stat.parentNode.removeChild(stat);
}, false);
document.getElementById("file-encrypted").appendChild(stat);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
let t0 = undefined;
Promise.all([
window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt', 'decrypt']
),
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function(event) {
const plaintext = new Uint8Array(this.result);
resolve(plaintext);
};
reader.onerror = function(err) {
reject(err);
};
})
]).then(([secretKey, plaintext]) => {
t0 = performance.now();
stat.querySelector(".mdl-card__supporting-text").innerHTML = `
<div class="mdl-spinner mdl-js-spinner is-active"></div>
encrypting...
`;
console.log("encrypting");
return Promise.all([
window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
tagLength: 128
},
secretKey,
plaintext
),
window.crypto.subtle.exportKey('jwk', secretKey)
]);
}).then(([encrypted, keydata]) => {
console.log("encrypted");
const encryptedURL = window.URL.createObjectURL(new Blob([encrypted]));
const encryptedFileName = file.name + ".encrypteddat";
stat.querySelector(".mdl-card__title").appendChild(document.createTextNode(encryptedFileName));
stat.querySelector(".mdl-card__supporting-text").innerHTML = `
<div data-paramname="k">
<label>
K (Secret Key):
<button class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">content_copy</i>
</button>
<input class="mdl-textfield__input" type="text" readonly="readonly" value="${keydata.k}" onfocus="this.select();">
</label>
</div>
<div data-paramname="iv">
<label>
IV (Inital Vector):
<button class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">content_copy</i>
</button>
<input class="mdl-textfield__input" type="text" readonly="readonly" value="${arrayToHex(iv)}" onfocus="this.select();">
</label>
</div>
<div>
Progressing time: ${(performance.now() - t0).toFixed(2)}ms
</div>
`;
for (let param of ["k", "iv"]) {
let button = stat.querySelector(`.mdl-card__supporting-text [data-paramname=${param}] button`);
let textfield = stat.querySelector(`.mdl-card__supporting-text [data-paramname=${param}] input[type=text]`);
button.addEventListener("click", (event) => {
event.preventDefault();
textfield.select();
if (document.execCommand("copy")) {
console.log("Copied!");
}
}, false);
}
stat.querySelector(".mdl-card__actions").innerHTML = `
<a href="${encryptedURL}" class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">file_download</i>
Download
</a>
`;
stat.querySelector(".mdl-card__actions a[href]").setAttribute("download", encryptedFileName);
stat.querySelector(".mdl-card__menu button").addEventListener("click", () => {
window.URL.revokeObjectURL(encryptedURL);
}, false);
return Promise.resolve(encryptedURL);
}).then((encryptedURL) => {
console.log("done : " + encryptedURL);
}).catch((error) => {
stat.querySelector(".mdl-card__title").innerHTML = "Error";
stat.querySelector(".mdl-card__supporting-text").innerHTML = "";
stat.querySelector(".mdl-card__supporting-text").appendChild(document.createTextNode(error.toString()));
stat.querySelector(".mdl-card__actions").innerHTML = "";
console.error(error);
}).then(() => {
document.getElementById("file-upload").value = "";
});
}, false);
document.getElementById("file-download").addEventListener("change", function (event) {
"use strict";
event.preventDefault();
const file = event.target.files[0];
const stat = document.createElement("div");
stat.setAttribute("class", "mdl-card mdl-shadow--2dp mdl-cell");
stat.innerHTML = `
<div class="mdl-card__title"></div>
<div class="mdl-card__supporting-text"></div>
<div class="mdl-card__actions"></div>
<div class="mdl-card__menu">
<button class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">clear</i>
</button>
</div>
`;
stat.querySelector(".mdl-card__menu button").addEventListener("click", () => {
stat.parentNode.removeChild(stat);
}, false);
document.getElementById("file-decrypted").appendChild(stat);
let t0 = undefined;
Promise.all([
new Promise((resolve, reject) => {
let k = window.prompt("K?", "");
if (typeof(k) === "string") {
k = k.trim();
} else {
return reject(new Error("Cancelled"));
}
let iv = window.prompt("IV?", "");
if (typeof(iv) === "string") {
iv = iv.trim();
} else {
return reject(new Error("Cancelled"));
}
resolve({k, iv});
}),
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function(event) {
const encryptedtext = new Uint8Array(this.result);
resolve(encryptedtext);
};
reader.onerror = function(err) {
reject(err);
};
})
]).then(([{k, iv}, data]) => {
t0 = performance.now();
console.log(`loaded: k=${k}, iv=${iv}`);
return Promise.all([
window.crypto.subtle.importKey(
'jwk',
{
kty: 'oct',
k,
alg: 'A128GCM',
ext: true
},
{
name: 'AES-GCM'
},
true,
['encrypt', 'decrypt']
),
Promise.resolve(iv),
Promise.resolve(data)
]);
}).then(([key, iv, data]) => {
stat.querySelector(".mdl-card__supporting-text").innerHTML = `
<div class="mdl-spinner mdl-js-spinner is-active"></div>
decrypting...
`;
console.log("decrypting");
return window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: hexToArray(iv),
tagLength: 128
},
key,
data
);
}).then((plaintext) => {
console.log("decrypted");
const decryptedURL = window.URL.createObjectURL(new Blob([plaintext]));
const decryptedFileName = file.name.replace(/\.encrypteddat$/, "");
stat.querySelector(".mdl-card__title").appendChild(document.createTextNode(decryptedFileName));
stat.querySelector(".mdl-card__supporting-text").innerHTML = `
<div>
Progressing time: ${(performance.now() - t0).toFixed(2)}ms
</div>
`;
stat.querySelector(".mdl-card__actions").innerHTML = `
<a href="${decryptedURL}" class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">file_download</i>
Download
</a>
`;
stat.querySelector(".mdl-card__actions a[href]").setAttribute("download", decryptedFileName);
stat.querySelector(".mdl-card__menu button").addEventListener("click", () => {
window.URL.revokeObjectURL(encryptedURL);
}, false);
return Promise.resolve(decryptedURL);
}).catch((error) => {
console.error(error);
stat.querySelector(".mdl-card__title").innerHTML = "Error";
stat.querySelector(".mdl-card__supporting-text").innerHTML = "";
stat.querySelector(".mdl-card__supporting-text").appendChild(document.createTextNode(error.toString()));
stat.querySelector(".mdl-card__actions").innerHTML = "";
console.error(error);
}).then(() => {
document.getElementById("file-download").value = "";
});
}, false);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment