Skip to content

Instantly share code, notes, and snippets.

@elierotenberg

elierotenberg/README.md

Last active Jan 11, 2020
Embed
What would you like to do?
Offline password generator bookmarklet

Notes:

  • password generation is purely offline; no risk of mitm
  • uses browser crypto API if available; fallback to Math.random
  1. Copy and paste the following URL in your browser address bar:

data:text/html;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+Cgo8aGVhZD4KICA8dGl0bGU+UGFzc3dvcmQgZ2VuZXJhdG9yPC90aXRsZT4KICA8c3R5bGU+CiAgICBsYWJlbCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQogIDwvc3R5bGU+CjwvaGVhZD4KCjxib2R5PgogIDxzY3JpcHQ+CiAgICBjb25zdCBnZW5lcmF0ZV9wYXNzd29yZCA9ICgpID0+IHsKICAgICAgY29uc3QgbGV0dGVycyA9ICJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaIjsKICAgICAgY29uc3QgZGlnaXRzID0gIjAxMjM0NTY3ODkiOwogICAgICBjb25zdCBzeW1ib2xzID0gIiUrJyEjJF4/OiwoKXt9W11+IjsKICAgICAgY29uc3QgcmFuZG9tSW50ID0gKG1pbiwgbWF4KSA9PiBtaW4gKyAod2luZG93LmNyeXB0byA/IHdpbmRvdy5jcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKG5ldyBVaW50MzJBcnJheSgxKSlbMF0gJSAobWF4IC0gbWluICsgMSkgOiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluICsgMSkpKTsKICAgICAgY29uc3QgcGljayA9ICh0KSA9PiB0W3JhbmRvbUludCgwLCB0Lmxlbmd0aCAtIDEpXTsKICAgICAgY29uc3QgcHJpbnQgPSAodGV4dCkgPT4gewogICAgICAgIGNvbnN0IHJlc3VsdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJyZXN1bHQiKTsKICAgICAgICByZXN1bHQudGV4dENvbnRlbnQgPSB0ZXh0OwogICAgICAgIHJlc3VsdC5mb2N1cygpOwogICAgICAgIHJlc3VsdC5zZWxlY3QoKTsKICAgICAgfTsKICAgICAgY29uc3QgaW5jbHVkZSA9IFtdOwogICAgICBpZiAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImluY2x1ZGUtbGV0dGVycyIpLmNoZWNrZWQpIHsKICAgICAgICBpbmNsdWRlLnB1c2gobGV0dGVycyk7CiAgICAgIH0KICAgICAgaWYgKGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJpbmNsdWRlLWRpZ2l0cyIpLmNoZWNrZWQpIHsKICAgICAgICBpbmNsdWRlLnB1c2goZGlnaXRzKTsKICAgICAgfQogICAgICBpZiAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImluY2x1ZGUtc3ltYm9scyIpLmNoZWNrZWQpIHsKICAgICAgICBpbmNsdWRlLnB1c2goc3ltYm9scyk7CiAgICAgIH0KICAgICAgY29uc3QgcGFzc3dvcmRMZW5ndGggPSBwYXJzZUludChkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicGFzc3dvcmQtbGVuZ3RoIikudmFsdWUpOwogICAgICBpZiAoaW5jbHVkZS5sZW5ndGggPT09IDApIHsKICAgICAgICByZXR1cm4gcHJpbnQoImluY2x1ZGUgYXQgbGVhc3Qgb25lIGtpbmQgb2YgY2hhcmFjdGVyIik7CiAgICAgIH0KICAgICAgbGV0IHBhc3N3b3JkID0gIiI7CiAgICAgIHdoaWxlIChwYXNzd29yZC5sZW5ndGggPCBwYXNzd29yZExlbmd0aCkgewogICAgICAgIGNvbnN0IGZyb20gPSBwaWNrKGluY2x1ZGUpOwogICAgICAgIHBhc3N3b3JkICs9IHBpY2soZnJvbSk7CiAgICAgIH0KICAgICAgcHJpbnQocGFzc3dvcmQpOwogICAgfTsKICA8L3NjcmlwdD4KICA8ZGl2PgogICAgPGxhYmVsPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImluY2x1ZGUtbGV0dGVycyIgY2hlY2tlZD5sZXR0ZXJzPC9sYWJlbD4KICAgIDxsYWJlbD48aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJpbmNsdWRlLWRpZ2l0cyIgY2hlY2tlZD5kaWdpdHM8L2xhYmVsPgogICAgPGxhYmVsPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImluY2x1ZGUtc3ltYm9scyIgY2hlY2tlZD5zeW1ib2xzPC9sYWJlbD4KICAgIDxsYWJlbD5sZW5ndGggPGlucHV0IHR5cGU9Im51bWJlciIgaWQ9InBhc3N3b3JkLWxlbmd0aCIgdmFsdWU9IjE4IiBtaW49IjEiIG1heD0iMjQiPjwvbGFiZWw+CiAgICA8bGFiZWw+PGJ1dHRvbiBvbmNsaWNrPSJqYXZhc2NyaXB0OmdlbmVyYXRlX3Bhc3N3b3JkKCkiPmdlbmVyYXRlPC9idXR0b24+PC9sYWJlbD4KICAgIDx0ZXh0YXJlYSBpZD0icmVzdWx0Ij48L3RleHRhcmVhPgogIDwvZGl2Pgo8L2JvZHk+Cgo8L2h0bWw+

(if you don't trust it, use any base 64 URL decode to check that is non malicious, e.g. using https://www.base64decode.org/)

  1. Bookmark it

  2. ...

  3. Profit

data:text/html;base64,PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+Cgo8aGVhZD4KICA8dGl0bGU+UGFzc3dvcmQgZ2VuZXJhdG9yPC90aXRsZT4KICA8c3R5bGU+CiAgICBsYWJlbCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQogIDwvc3R5bGU+CjwvaGVhZD4KCjxib2R5PgogIDxzY3JpcHQ+CiAgICBjb25zdCBnZW5lcmF0ZV9wYXNzd29yZCA9ICgpID0+IHsKICAgICAgY29uc3QgbGV0dGVycyA9ICJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ekFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaIjsKICAgICAgY29uc3QgZGlnaXRzID0gIjAxMjM0NTY3ODkiOwogICAgICBjb25zdCBzeW1ib2xzID0gIiUrJyEjJF4/OiwoKXt9W11+IjsKICAgICAgY29uc3QgcmFuZG9tSW50ID0gKG1pbiwgbWF4KSA9PiBtaW4gKyAod2luZG93LmNyeXB0byA/IHdpbmRvdy5jcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKG5ldyBVaW50MzJBcnJheSgxKSlbMF0gJSAobWF4IC0gbWluICsgMSkgOiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluICsgMSkpKTsKICAgICAgY29uc3QgcGljayA9ICh0KSA9PiB0W3JhbmRvbUludCgwLCB0Lmxlbmd0aCAtIDEpXTsKICAgICAgY29uc3QgcHJpbnQgPSAodGV4dCkgPT4gewogICAgICAgIGNvbnN0IHJlc3VsdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJyZXN1bHQiKTsKICAgICAgICByZXN1bHQudGV4dENvbnRlbnQgPSB0ZXh0OwogICAgICAgIHJlc3VsdC5mb2N1cygpOwogICAgICAgIHJlc3VsdC5zZWxlY3QoKTsKICAgICAgfTsKICAgICAgY29uc3QgaW5jbHVkZSA9IFtdOwogICAgICBpZiAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImluY2x1ZGUtbGV0dGVycyIpLmNoZWNrZWQpIHsKICAgICAgICBpbmNsdWRlLnB1c2gobGV0dGVycyk7CiAgICAgIH0KICAgICAgaWYgKGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJpbmNsdWRlLWRpZ2l0cyIpLmNoZWNrZWQpIHsKICAgICAgICBpbmNsdWRlLnB1c2goZGlnaXRzKTsKICAgICAgfQogICAgICBpZiAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImluY2x1ZGUtc3ltYm9scyIpLmNoZWNrZWQpIHsKICAgICAgICBpbmNsdWRlLnB1c2goc3ltYm9scyk7CiAgICAgIH0KICAgICAgY29uc3QgcGFzc3dvcmRMZW5ndGggPSBwYXJzZUludChkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicGFzc3dvcmQtbGVuZ3RoIikudmFsdWUpOwogICAgICBpZiAoaW5jbHVkZS5sZW5ndGggPT09IDApIHsKICAgICAgICByZXR1cm4gcHJpbnQoImluY2x1ZGUgYXQgbGVhc3Qgb25lIGtpbmQgb2YgY2hhcmFjdGVyIik7CiAgICAgIH0KICAgICAgbGV0IHBhc3N3b3JkID0gIiI7CiAgICAgIHdoaWxlIChwYXNzd29yZC5sZW5ndGggPCBwYXNzd29yZExlbmd0aCkgewogICAgICAgIGNvbnN0IGZyb20gPSBwaWNrKGluY2x1ZGUpOwogICAgICAgIHBhc3N3b3JkICs9IHBpY2soZnJvbSk7CiAgICAgIH0KICAgICAgcHJpbnQocGFzc3dvcmQpOwogICAgfTsKICA8L3NjcmlwdD4KICA8ZGl2PgogICAgPGxhYmVsPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImluY2x1ZGUtbGV0dGVycyIgY2hlY2tlZD5sZXR0ZXJzPC9sYWJlbD4KICAgIDxsYWJlbD48aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJpbmNsdWRlLWRpZ2l0cyIgY2hlY2tlZD5kaWdpdHM8L2xhYmVsPgogICAgPGxhYmVsPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImluY2x1ZGUtc3ltYm9scyIgY2hlY2tlZD5zeW1ib2xzPC9sYWJlbD4KICAgIDxsYWJlbD5sZW5ndGggPGlucHV0IHR5cGU9Im51bWJlciIgaWQ9InBhc3N3b3JkLWxlbmd0aCIgdmFsdWU9IjE4IiBtaW49IjEiIG1heD0iMjQiPjwvbGFiZWw+CiAgICA8bGFiZWw+PGJ1dHRvbiBvbmNsaWNrPSJqYXZhc2NyaXB0OmdlbmVyYXRlX3Bhc3N3b3JkKCkiPmdlbmVyYXRlPC9idXR0b24+PC9sYWJlbD4KICAgIDx0ZXh0YXJlYSBpZD0icmVzdWx0Ij48L3RleHRhcmVhPgogIDwvZGl2Pgo8L2JvZHk+Cgo8L2h0bWw+
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<head>
<title>Password generator</title>
<style>
label {
display: block;
}
</style>
</head>
<body>
<script>
const generate_password = () => {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const digits = "0123456789";
const symbols = "%+'!#$^?:,(){}[]~";
const randomInt = (min, max) => min + (window.crypto ? window.crypto.getRandomValues(new Uint32Array(1))[0] % (max - min + 1) : Math.floor(Math.random() * (max - min + 1)));
const pick = (t) => t[randomInt(0, t.length - 1)];
const print = (text) => {
const result = document.getElementById("result");
result.textContent = text;
result.focus();
result.select();
};
const include = [];
if (document.getElementById("include-letters").checked) {
include.push(letters);
}
if (document.getElementById("include-digits").checked) {
include.push(digits);
}
if (document.getElementById("include-symbols").checked) {
include.push(symbols);
}
const passwordLength = parseInt(document.getElementById("password-length").value);
if (include.length === 0) {
return print("include at least one kind of character");
}
let password = "";
while (password.length < passwordLength) {
const from = pick(include);
password += pick(from);
}
print(password);
};
</script>
<div>
<label><input type="checkbox" id="include-letters" checked>letters</label>
<label><input type="checkbox" id="include-digits" checked>digits</label>
<label><input type="checkbox" id="include-symbols" checked>symbols</label>
<label>length <input type="number" id="password-length" value="18" min="1" max="24"></label>
<label><button onclick="javascript:generate_password()">generate</button></label>
<textarea id="result"></textarea>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.