243 lines
No EOL
9.2 KiB
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>
|
|
|
|
</output>
|
|
</label>
|
|
<label id="entropy">
|
|
Lösenordsentropi
|
|
<output>
|
|
|
|
</output>
|
|
</label>
|
|
<label id="brute-force-estimate">
|
|
Brute force max <b title="1000 försök per sekund">?</b>
|
|
<output>
|
|
|
|
</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 = " ";
|
|
entropyOutput.innerHTML = " ";
|
|
bruteforceEstimateOutput.innerHTML = " ";
|
|
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> |