gists/2025.xkcd-password-strength.html

243 lines
No EOL
9.2 KiB
HTML

<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>Password Entropy calculator</title>
<style>
html {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
margin: 3em 1em;
}
form {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem 1rem;
}
input[type=range] {
display: block;
}
label[id] > output {
font-size: 2rem;
display: block;
border: 1px solid lightseagreen;
background-color: lightgreen;
padding: 0.5rem;
overflow: auto;
white-space: nowrap;
max-width: 100%;
box-sizing: border-box;
}
fieldset > label {
display: flex;
gap: 0.25em;
align-items: flex-start;
}
fieldset input {
display: inline-block;
transform: translateY(0.15em);
}
footer {
color: gray;
font-size: small;
margin-top: 3em;
text-align: center;
}
.standard > label:has(#common-words),
.standard > label:has(#common-words-pool),
.standard > label:has(#dictionary-pool),
.xkcd-symbols > label:has(#lowercase-latin-letters),
.xkcd-symbols > label:has(#uppercase-latin-letters),
.xkcd-symbols > label:has(#digits),
.xkcd-symbols > label:has(#special-chars),
.xkcd-symbols > label:has(#special-chars-pool),
.xkcd-symbols > label:has(#dictionary-pool),
.xkcd-dictionary > label:has(#common-words),
.xkcd-dictionary > label:has(#special-chars-pool),
.xkcd-dictionary > label:has(#common-words-pool) {
display: none;
}
fieldset {
grid-column: 1 / 4;
}
small {
color: gray;
}
b {
cursor: help;
}
</style>
</head>
<body>
<h1>XKCD Password strength demo</h1>
<form class="standard">
<fieldset>
<legend>Formel</legend>
<label>
<input type="radio" checked value="standard" id="formula-standard" name="formula">
<span>Standard (<code>log2(urval^längd)</code>)</span>
</label>
<label>
<input type="radio" value="xkcd-dictionary" id="formula-xkcd-dictionary" name="formula">
<span>XKCD uppslagsord + extra versaler, gemener eller specialtecken (<code>log2(65000*urval*urval)</code>)</span>
</label>
<label>
<input type="radio" value="xkcd-symbols" id="formula-xkcd-symbols" name="formula">
<span>XKCD symboler av vanliga ord (<code>log2(urval*antal)</code>)</span>
</label>
</fieldset>
<label id="length">
Lösenordslängd
<output>
&nbsp;
</output>
</label>
<label id="entropy">
Lösenordsentropi
<output>
&nbsp;
</output>
</label>
<label id="brute-force-estimate">
Brute force max <b title="1000 försök per sekund">?</b>
<output>
&nbsp;
</output>
</label>
<label>
<input type="range" step="1" max="50" value="0" id="lowercase-latin-letters" name="lowercase-latin-letters">
Latinska gemener <small>(a-z)</small>: <output></output>
</label>
<label>
<input type="range" step="1" max="50" value="0" id="uppercase-latin-letters" name="uppercase-latin-letters">
Latinska versaler <small>(A-Z)</small>: <output></output>
</label>
<label>
<input type="range" step="1" max="50" value="0" id="digits" name="digits">
Siffror <small>(0-9)</small>: <output></output>
</label>
<label>
<input type="range" step="1" max="50" value="0" id="special-chars" name="special-chars">
"Specialtecken" <small>(inkl Å, Ä och Ö)</small>: <output></output>
</label>
<label>
<input type="range" step="1" max="50" value="30" id="special-chars-pool" name="special-chars-pool">
Specialteckensurval: <output>30</output>
</label>
<label>
<input type="range" step="1" max="10" value="0" id="common-words" name="common-words">
Vanliga ord: <output></output>
</label>
<label>
<input type="range" step="128" max="4096" value="2048" id="common-words-pool" name="common-words-pool">
Vanliga ord-urval: <output>2048</output>
</label>
<label>
<input type="range" step="1000" max="100000" value="65000" id="dictionary-pool" name="dictionary-pool">
Uppslagsordsurval: <output>65000</output>
</label>
</form>
<footer>
<p>
Baserad på <a href="https://www.omnicalculator.com/other/password-entropy#password-entropy-is-not-all-that-matters">Omni Password Entropy Calculator</a>
men modifierad för att verifiera <a href="https://www.explainxkcd.com/wiki/index.php/936:_Password_Strength">XKCD 936</a>.
<br>Skapad okt 2025 av <a href="https://madr.se">madr</a>
</p>
</footer>
<script>
(function(G){
const $v = (id) => parseInt(document.getElementById(id).value, 10);
const inputs = document.querySelectorAll("input");
const lengthOutput = document.querySelector("#length > output");
const entropyOutput = document.querySelector("#entropy > output");
const bruteforceEstimateOutput = document.querySelector("#brute-force-estimate > output");
let mode = "standard";
calcBFE = (entropy) => {
let bfe = (2**entropy) / 1000
if (bfe < 1.0) {
return "<1s"
}
if (bfe < 60.0) {
return `~ ${Math.round(bfe)} sek`
}
bfe = bfe / 60
if (bfe < 60.0) {
return `~ ${Math.round(bfe)} min`
}
bfe = bfe / 60
if (bfe < 24.0) {
return `~ ${Math.round(bfe)} tim`
}
bfe = bfe / 24
if (bfe < 7.0) {
return `~ ${Math.round(bfe)} dgr`
}
bfe = bfe / 7
if (bfe < 52.0) {
return `~ ${Math.round(bfe)} v`
}
bfe = bfe * 7 / 30
if (bfe < 12.0) {
return `~ ${Math.round(bfe)} mån`
}
bfe = bfe * 30 / 365
if (bfe < 100.0) {
return `~ ${Math.round(bfe)} år`
}
return "> 100 år"
}
const calcEntropy = ({target: elm}) => {
if (elm.type == "range") {
elm.parentNode.querySelector("output").innerHTML = elm.value;
} else {
elm.form.className = elm.value;
mode = elm.value;
}
let a, b, entropy;
const c = 27 * !!$v("lowercase-latin-letters") + 27 * !!$v("uppercase-latin-letters") + 10 * !!$v("digits") + 30 * !!$v("special-chars");
if (mode != "xkcd-symbols" && !c) {
lengthOutput.innerHTML = "&nbsp;";
entropyOutput.innerHTML = "&nbsp;";
bruteforceEstimateOutput.innerHTML = "&nbsp;";
return;
}
if (mode == "standard") {
a = c;
b = $v("special-chars") + $v("digits") + $v("uppercase-latin-letters") + $v("lowercase-latin-letters");
entropy = Math.floor(Math.log2(a**b));
} else if (mode == "xkcd-dictionary") {
a = $v("dictionary-pool");
b = c;
entropy = Math.floor(Math.log2(a*b*b));
} else if (mode == "xkcd-symbols") {
a = $v("common-words-pool");
b = $v("common-words");
entropy = Math.floor(Math.log2(a**b));
}
const passwordLength = (mode != "standard") ? "*" : $v("special-chars") + $v("digits") + $v("uppercase-latin-letters") + $v("lowercase-latin-letters");
lengthOutput.innerHTML = "";
lengthOutput.insertAdjacentHTML("afterbegin", passwordLength);
entropyOutput.innerHTML = "";
entropyOutput.insertAdjacentHTML("afterbegin", entropy);
bruteforceEstimateOutput.innerHTML = "";
bruteforceEstimateOutput.insertAdjacentHTML("afterbegin", calcBFE(entropy));
};
inputs.forEach(function(elm) {
elm.onchange = (e) => calcEntropy(e)
})
}(this))
</script>
</body>
</html>