1102 lines
37 KiB
HTML
1102 lines
37 KiB
HTML
<html><head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
<meta name="description" content="Password Strength Tester : Calculates the entropy of a password">
|
|
|
|
<style>
|
|
a,
|
|
b,
|
|
body,
|
|
center,
|
|
code,
|
|
div,
|
|
form,
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6,
|
|
header,
|
|
html,
|
|
i,
|
|
object,
|
|
p,
|
|
s,
|
|
section,
|
|
span,
|
|
strong,
|
|
table,
|
|
tbody,
|
|
td,
|
|
time,
|
|
tr,
|
|
var {
|
|
margin: 0;
|
|
padding: 0;
|
|
border: 0;
|
|
font: inherit;
|
|
vertical-align: baseline
|
|
}
|
|
|
|
header,
|
|
section {
|
|
display: block
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
border-spacing: 0
|
|
}
|
|
|
|
#charset {
|
|
border: hidden
|
|
}
|
|
|
|
body {
|
|
box-sizing: border-box;
|
|
color: #373737;
|
|
background: #212121;
|
|
font-size: 16px;
|
|
font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif;
|
|
line-height: 1.5;
|
|
-webkit-font-smoothing: antialiased
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
margin: 10px 0;
|
|
font-weight: 700;
|
|
color: #222;
|
|
font-family: 'Lucida Grande', Calibri, Helvetica, Arial, sans-serif;
|
|
letter-spacing: -1px
|
|
}
|
|
|
|
h1 {
|
|
font-size: 36px;
|
|
font-weight: 700
|
|
}
|
|
|
|
h2 {
|
|
padding-bottom: 10px;
|
|
font-size: 32px;
|
|
background: url(../images/bg_hr.png) repeat-x bottom
|
|
}
|
|
|
|
h3 {
|
|
font-size: 24px
|
|
}
|
|
|
|
h4 {
|
|
font-size: 21px
|
|
}
|
|
|
|
h5 {
|
|
font-size: 18px
|
|
}
|
|
|
|
h6 {
|
|
font-size: 16px
|
|
}
|
|
|
|
p {
|
|
margin: 10px 0 10px 0
|
|
}
|
|
|
|
a {
|
|
text-decoration: none;
|
|
color: #007edf;
|
|
text-shadow: none;
|
|
transition: color .5s ease;
|
|
transition: text-shadow .5s ease;
|
|
-webkit-transition: color .5s ease;
|
|
-webkit-transition: text-shadow .5s ease;
|
|
-moz-transition: color .5s ease;
|
|
-moz-transition: text-shadow .5s ease;
|
|
-o-transition: color .5s ease;
|
|
-o-transition: text-shadow .5s ease;
|
|
-ms-transition: color .5s ease;
|
|
-ms-transition: text-shadow .5s ease
|
|
}
|
|
|
|
a:focus,
|
|
a:hover {
|
|
text-decoration: underline
|
|
}
|
|
|
|
strong {
|
|
font-weight: 700
|
|
}
|
|
|
|
code {
|
|
width: 100%;
|
|
color: #222;
|
|
background-color: #fff;
|
|
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
|
font-size: 14px;
|
|
border-radius: 2px;
|
|
-moz-border-radius: 2px;
|
|
-webkit-border-radius: 2px
|
|
}
|
|
|
|
code {
|
|
padding: 3px;
|
|
margin: 0 3px;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, .1)
|
|
}
|
|
|
|
#info {
|
|
border: 2px solid #373737;
|
|
text-align: left
|
|
}
|
|
|
|
td {
|
|
padding: 10px;
|
|
border: 1px solid #373737
|
|
}
|
|
|
|
form {
|
|
background: #f2f2f2;
|
|
padding: 20px
|
|
}
|
|
|
|
.outer {
|
|
width: 100%
|
|
}
|
|
|
|
.inner {
|
|
position: relative;
|
|
max-width: 640px;
|
|
padding: 20px 10px;
|
|
margin: 0 auto
|
|
}
|
|
|
|
#header_wrap {
|
|
background: #212121;
|
|
background: -moz-linear-gradient(top, #373737, #212121);
|
|
background: -webkit-linear-gradient(top, #373737, #212121);
|
|
background: -ms-linear-gradient(top, #373737, #212121);
|
|
background: -o-linear-gradient(top, #373737, #212121);
|
|
background: linear-gradient(top, #373737, #212121)
|
|
}
|
|
|
|
#header_wrap .inner {
|
|
padding: 50px 10px 30px 10px
|
|
}
|
|
|
|
#project_title {
|
|
margin: 0;
|
|
color: #fff;
|
|
font-size: 42px;
|
|
font-weight: 700;
|
|
text-shadow: #111 0 0 10px
|
|
}
|
|
|
|
#main_content_wrap {
|
|
background: #f2f2f2;
|
|
border-top: 1px solid #111;
|
|
border-bottom: 1px solid #111
|
|
}
|
|
|
|
#main_content {
|
|
padding-top: 40px;
|
|
height: 1100px
|
|
}
|
|
|
|
@media screen and (max-width:480px) {
|
|
body {
|
|
font-size: 14px
|
|
}
|
|
.inner {
|
|
min-width: 320px;
|
|
max-width: 480px
|
|
}
|
|
#project_title {
|
|
font-size: 32px
|
|
}
|
|
h1 {
|
|
font-size: 28px
|
|
}
|
|
h2 {
|
|
font-size: 24px
|
|
}
|
|
h3 {
|
|
font-size: 21px
|
|
}
|
|
h4 {
|
|
font-size: 18px
|
|
}
|
|
h5 {
|
|
font-size: 14px
|
|
}
|
|
h6 {
|
|
font-size: 12px
|
|
}
|
|
code {
|
|
min-width: 320px;
|
|
max-width: 480px;
|
|
font-size: 11px
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<title>Password Strength Tester</title>
|
|
<script>
|
|
// fid-umd {"name":"PasswordStrength","jslint":1}
|
|
/*global module, exports, define, modulejs, YUI, console */
|
|
(function (name, root, factory) {
|
|
"use strict";
|
|
function isObject(x) { return typeof x === "object"; }
|
|
if ((typeof module)[0] === "o" && isObject(module.exports)) {
|
|
module.exports = factory();
|
|
} else if ((typeof exports)[0] === "o") {
|
|
exports[name] = factory();
|
|
} else if (isObject(root.define) && root.define.amd) {
|
|
root.define(name, [], factory);
|
|
} else if (isObject(root.modulejs)) {
|
|
root.modulejs.define(name, factory);
|
|
} else if (isObject(root.YUI)) {
|
|
root.YUI.add(name, function (Y) { Y[name] = factory(); });
|
|
} else {
|
|
root[name] = factory();
|
|
}
|
|
}("PasswordStrength", this, function () {
|
|
"use strict";
|
|
// fid-umd end
|
|
|
|
/**
|
|
* Shim the Math object to support log2 if it does not already.
|
|
*/
|
|
if (!Math.log2) {
|
|
Math.log2 = function (n) {
|
|
return Math.log(n) / Math.log(2);
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* Iterates over an object
|
|
*
|
|
* @param {Object} obj
|
|
* @param {Function(key,value)} callback
|
|
*/
|
|
function objectForEach(obj, callback) {
|
|
Object.keys(obj).forEach(function (key) {
|
|
callback(key, obj[key]);
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Turns a list of characters into a regular expression that will match
|
|
* whatever you pass in. There may be trouble generating the pattern,
|
|
* so here we are escaping every character that isn't alphanumeric.
|
|
*
|
|
* @param {string} letters
|
|
* @param {string} flags (defaults to "")
|
|
* @return {RegExp} pattern
|
|
*/
|
|
function safeCharMatcher(letters, flags) {
|
|
return new RegExp("[" + letters.replace(/[\W_]/g, function (x) { return "\\" + x; }) + "]", flags || "");
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine the strength of a password.
|
|
*
|
|
* Usage:
|
|
*
|
|
* ps = new PasswordStrength();
|
|
*
|
|
* // Improve checks by adding additional data files - optional
|
|
* ps.addCommonPasswords(pass); // data/common-passwords.json
|
|
* ps.addTrigraphMap(tri); // data/trigraphs.json
|
|
*
|
|
* // Calculate password strength
|
|
* stats = ps.check("my password");
|
|
*
|
|
* @return {PasswordStrength} instance
|
|
*/
|
|
function PasswordStrength() {
|
|
if (!(this instanceof PasswordStrength)) {
|
|
return new PasswordStrength();
|
|
}
|
|
|
|
this.CommonPasswords = null;
|
|
this.trigraph = null;
|
|
|
|
var Use33 = document.getElementById("33");
|
|
|
|
if (Use33.checked == true){
|
|
|
|
window.SymbolCharsets = " !#$%∞&'()*+,./:;<=>?@[]^_`{|}~-\""
|
|
|
|
} else {
|
|
window.SymbolCharsets = " !#$%&'()*+,./:;<=>?@[]^_`{|}~-\""
|
|
}
|
|
|
|
this.Charsets = {
|
|
Number: "0123456789",
|
|
Lowercase: "abcdefghijklmnopqrstuvwxyz",
|
|
Uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
Symbol: window.SymbolCharsets
|
|
};
|
|
window.Charsetscheck = this.Charsets
|
|
/**
|
|
* Sets CommonPassword property with what a user passes in.
|
|
* Uses it if it's an array, processes it into an array if it's a
|
|
* string and throws an error if it's not a format we can work with.
|
|
*
|
|
* Returns the PasswordCheck object so it can be chained
|
|
*
|
|
* @param {(Array|string)} passwords
|
|
* @return {PasswordStrength} PasswordStrength
|
|
*/
|
|
this.addCommonPasswords = function (passwords) {
|
|
if (passwords) {
|
|
if (Array.isArray(passwords)) {
|
|
this.CommonPasswords = passwords;
|
|
} else if (typeof passwords === "string") {
|
|
this.CommonPasswords = passwords.split(/\r\n|\r|\n/);
|
|
} else {
|
|
throw new Error("Format does not match any expected format.");
|
|
}
|
|
} else {
|
|
this.CommonPasswords = [];
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
/**
|
|
* TrigraphMap is an ojbect representing entries of 3 letter
|
|
* combinations and how often they appear.
|
|
*
|
|
* {
|
|
* "_ty": 123,
|
|
* "tyl": 22,
|
|
* "yle": 19,
|
|
* "ler": 5,
|
|
* "er_": 543
|
|
* }
|
|
*
|
|
* For further explanation on the numbers, check out the documentation
|
|
* in the data folder.
|
|
*
|
|
* @typedef {Object} PasswordStrength~trigraphMap
|
|
* @property {number} * Frequencies of that letter combo.
|
|
*/
|
|
|
|
/**
|
|
* Returns the PasswordStrength object so it can be chained
|
|
*
|
|
* TA: Again, define Object better. Make a type for
|
|
* PasswordStrength~trigraphMap.
|
|
*
|
|
* @param {PasswordStrength~trigraphMap} trigraphMap
|
|
* @return {PasswordStrength} PasswordStrength
|
|
*/
|
|
this.addTrigraphMap = function (trigraphMap) {
|
|
if (trigraphMap) {
|
|
if (trigraphMap && typeof trigraphMap === "object" && !Array.isArray(trigraphMap)) {
|
|
this.trigraph = trigraphMap;
|
|
} else {
|
|
throw new Error("Format does not match any expected format.");
|
|
}
|
|
} else {
|
|
this.trigraph = null;
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
/**
|
|
* charsetGroups is an object of different group of defined characters
|
|
* used in figuring out how many characters per group as well as
|
|
* what characters fall into each group. It is based on a
|
|
* PasswordStrength instance's `this.Charsets` object. When a password
|
|
* has a character that matches one of the character sets in the
|
|
* PasswordStrength instance's sets, this sets that key to `true`.
|
|
*
|
|
* The "other" key is all letters that did not match another set.
|
|
*
|
|
* Example for the password "abcd£1234":
|
|
*
|
|
* {
|
|
* lower: true,
|
|
* upper: false,
|
|
* number: true,
|
|
* punctuation: false,
|
|
* symbol: false,
|
|
* other: "£"
|
|
* }
|
|
*
|
|
* @typedef {Object} PasswordStrength~charsetGroups
|
|
* @property {string} other Characters that did not match another set
|
|
* @property {boolean} * All of the rest of the sets just set flags
|
|
*/
|
|
|
|
/**
|
|
* Gets the character set size of the current password.
|
|
* This breaks up the each character set and tests the password
|
|
* to see which set they belong in.
|
|
*
|
|
* @param {string} passToCheck
|
|
* @return {PasswordStrength~charsetGroups} groups
|
|
*/
|
|
this.charsetGroups = function (passToCheck) {
|
|
var groups;
|
|
|
|
groups = {};
|
|
objectForEach(this.Charsets, function (key, value) {
|
|
groups[key] = safeCharMatcher(value).test(passToCheck);
|
|
});
|
|
groups.other = this.otherChars(passToCheck);
|
|
|
|
return groups;
|
|
};
|
|
|
|
/**
|
|
* Calculates the size of each flag found for a password,
|
|
* and will get the flags if a password was passed in.
|
|
*
|
|
* @param {PasswordStrength~charsetGroups} groups
|
|
* @return {number} result
|
|
*/
|
|
this.Charsetsize = function (groups) {
|
|
var size;
|
|
|
|
size = 0;
|
|
|
|
objectForEach(this.Charsets, function (key, value) {
|
|
/*jslint unparam:true*/
|
|
if (groups[key]) {
|
|
size += value.length;
|
|
}
|
|
});
|
|
|
|
if (typeof groups.other === "string") {
|
|
size += groups.other.length;
|
|
}
|
|
|
|
return size;
|
|
};
|
|
|
|
/**
|
|
* Statistics is and object with information about the current state of
|
|
* the password.
|
|
*
|
|
* @typedef {Object} PasswordStrength~statistics
|
|
* @property {number} Charsetsize size of the characters from each
|
|
* group the characters fall into.
|
|
* @property {boolean} CommonPassword
|
|
* @property {number} PasswordLength the length of the current password
|
|
* @property {number} ShannonEntropyBits
|
|
* @property {string} strength how the strong the calculated password is
|
|
* estimated to be
|
|
* @property {(null|number)} TrigraphEntropyBits
|
|
*/
|
|
|
|
/**
|
|
* Calculates a bunch of statistics about a given password.
|
|
*
|
|
* TA: Define PasswordStrength~statistics
|
|
*
|
|
* @param {string} password
|
|
* @return {PasswordStrength~statistics}
|
|
*/
|
|
this.check = function (currentPassword) {
|
|
var result;
|
|
|
|
result = {
|
|
Charsetsize: 0,
|
|
CommonPassword: false,
|
|
PasswordLength: 0,
|
|
ShannonEntropyBits: 0,
|
|
StrengthCode: null,
|
|
TrigraphEntropyBits: 0,
|
|
Charsets: 0
|
|
};
|
|
|
|
// For undefined and if we get an empty value
|
|
if (!currentPassword || !currentPassword.length) {
|
|
if (this.trigraph) {
|
|
result.TrigraphEntropyBits = 0;
|
|
result.StrengthCode = "None";
|
|
result.Charsets = "None";
|
|
|
|
}
|
|
|
|
if (window.CommonPassword = true) {
|
|
result.CommonPassword = "yes";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
result.CommonPassword = this.checkCommonPasswords(currentPassword);
|
|
result.Charsets = this.charsetGroups(currentPassword);
|
|
result.Charsetsize = this.Charsetsize(result.Charsets);
|
|
result.ShannonEntropyBits = this.shannonScore(currentPassword);
|
|
result.PasswordLength = currentPassword.length;
|
|
result.TrigraphEntropyBits = this.checkTrigraph(currentPassword, result.Charsetsize);
|
|
result.StrengthCode = this.determineStrength(result);
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* This checks to see if the password is in the common list of
|
|
* passwords. If in the common passwords lists, sets the warn flag.
|
|
*
|
|
* @param {string} passToCheck
|
|
* @return {boolean}
|
|
*/
|
|
this.checkCommonPasswords = function (passToCheck) {
|
|
var i, CommonPasswordsLength, CommonPasswordsList;
|
|
|
|
passToCheck = passToCheck.toLowerCase();
|
|
|
|
if (this.CommonPasswords && this.CommonPasswords.length) {
|
|
CommonPasswordsList = this.CommonPasswords;
|
|
CommonPasswordsLength = this.CommonPasswords.length;
|
|
|
|
for (i = 0; i < CommonPasswordsLength; i += 1) {
|
|
if (CommonPasswordsList[i] === passToCheck) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
|
|
/**
|
|
* Compares the password to the trigraph to get the rating of how
|
|
* guessable the password is.
|
|
*
|
|
* @param {string} passToCheck
|
|
* @param {number} Charsetsize
|
|
* @return {number} entropy in bits
|
|
*/
|
|
this.checkTrigraph = function (passToCheck, Charsetsize) {
|
|
var score, i, str;
|
|
|
|
if (!this.trigraph) {
|
|
return null;
|
|
}
|
|
|
|
score = 1;
|
|
passToCheck = passToCheck.toLowerCase().replace(/[\W_]/gi, " ").trim();
|
|
passToCheck = "_" + passToCheck + "_";
|
|
|
|
for (i = 0; i < passToCheck.length - 2; i += 1) {
|
|
str = passToCheck.substr(i, 3);
|
|
|
|
if (this.trigraph[str]) {
|
|
// Less than fully random
|
|
score *= (1.0 - (this.trigraph[str] / 10000)) * Charsetsize;
|
|
} else {
|
|
// Fully random selection
|
|
score *= Charsetsize;
|
|
}
|
|
}
|
|
|
|
return Math.log2(score);
|
|
};
|
|
|
|
|
|
/**
|
|
* Takes parameters and calculates the strength of a given password
|
|
* from a list of codes set. This will use the trigraph entropy bits if
|
|
* available and fallback to the shannon entropy bits which we should
|
|
* always have.
|
|
*
|
|
* @param {Object} status
|
|
* @return {string} StrengthCode
|
|
*/
|
|
this.determineStrength = function (status) {
|
|
var StrengthCode, entropyBits,
|
|
StrengthCode = "";
|
|
|
|
if (!status.TrigraphEntropyBits) {
|
|
entropyBits = status.ShannonEntropyBits;
|
|
} else {
|
|
entropyBits = status.TrigraphEntropyBits;
|
|
}
|
|
|
|
if (entropyBits <= 20) {
|
|
StrengthCode = "<font color=red>Extremely Weak</font>"; // Keep out a typical attacker for minutes
|
|
} else if (entropyBits <= 32) {
|
|
StrengthCode = "<font color=red>Very Weak</font>"; // Crackable by a typical home computer in a week.
|
|
} else if (entropyBits <= 48) {
|
|
StrengthCode = "<font color=red>Weak</font>"; // Crackable by a typical home computer in a week.
|
|
} else if (entropyBits <= 64) {
|
|
StrengthCode = "<font color=brown>Reasonable</font>"; // A specialized computer could get this in one year.
|
|
} else if (entropyBits <= 80) {
|
|
StrengthCode = "<font color=green>Strong</font>"; // Resistant to a large, coordinated attack (botnet) for over a year.
|
|
} else if (entropyBits <= 500) {
|
|
// Nearly impossible to brute force, given more than all of the computing power in the world,
|
|
// optimized algorithms, specialized hardware and a thousand years.
|
|
StrengthCode = "<font color=blue>Very Strong</font>";
|
|
} else if (entropyBits <= 1023) {
|
|
// Nearly impossible to brute force, given more than all of the computing power in the world,
|
|
// optimized algorithms, specialized hardware and thousands years.
|
|
StrengthCode = "<font color=orange>Insanely Strong</font>";
|
|
} else {
|
|
// impossible,
|
|
StrengthCode = "<font color=purple>404 Incomprehensibly Strong</font>";
|
|
}
|
|
|
|
return StrengthCode;
|
|
};
|
|
|
|
|
|
/**
|
|
* Calls function to see if there are other characters used
|
|
* which we don't explicitly check for.
|
|
*
|
|
* @param {string} passToCheck
|
|
* @return {string} result
|
|
*/
|
|
this.otherChars = function (passToCheck) {
|
|
var chars, hash, pattern;
|
|
|
|
chars = "";
|
|
objectForEach(this.Charsets, function (key, value) {
|
|
/*jslint unparam:true*/
|
|
chars += value;
|
|
});
|
|
pattern = safeCharMatcher(chars, 'g');
|
|
hash = {};
|
|
passToCheck.replace(pattern, '').split('').forEach(function (letter) {
|
|
hash[letter] = true;
|
|
});
|
|
|
|
return Object.keys(hash).join('');
|
|
};
|
|
|
|
|
|
/**
|
|
* Calculate the Shannon Entropy value of a password. This is really
|
|
* the number of bits necessary to represent the possible characters
|
|
* times the length of the password.
|
|
*
|
|
* Reworked from code under a MIT license.
|
|
* entropy.js MIT License © 2014 James Abney http://github.com/jabney
|
|
*
|
|
* @param {string} pass Password to evaluate
|
|
* @return {number} Bits of entropy
|
|
*/
|
|
this.shannonScore = function (pass) {
|
|
var freq, passLength, sum;
|
|
|
|
function countFrequencies() {
|
|
var c, h, i;
|
|
|
|
h = {}; // Hash for collecting frequencies
|
|
|
|
for (i = 0; i < passLength; i += 1) {
|
|
c = pass.charAt(i);
|
|
|
|
if (!h[c]) {
|
|
h[c] = 1;
|
|
} else {
|
|
h[c] += 1;
|
|
}
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
sum = 0;
|
|
passLength = pass.length;
|
|
freq = countFrequencies();
|
|
objectForEach(freq, function (key, value) {
|
|
/*jslint unparam:true*/
|
|
var score;
|
|
|
|
score = value / passLength;
|
|
sum -= score * Math.log2(score);
|
|
});
|
|
|
|
return sum * passLength;
|
|
};
|
|
}
|
|
|
|
return PasswordStrength;
|
|
// fid-umd post
|
|
}));
|
|
// fid-umd post-end
|
|
</script>
|
|
<script>
|
|
window.onload = function () {
|
|
"use strict";
|
|
|
|
var commonXhttp, frequencyXhttp, passwordField, CustomScenarioField, passwordInfo, passwordStatistics, passwordStrength;
|
|
|
|
function recalc(test3) {
|
|
var ps, stats, Charsetsize, PasswordLengthtext, CommonPassword, CommonPasswordvalue, PasswordLength, ShannonEntropyBits, TrigraphEntropyBits, StrengthCode, Charsets;
|
|
|
|
ps = passwordStrength.check(passwordField.value);
|
|
window.ps = passwordStrength.check(passwordField.value);
|
|
stats = "";
|
|
Charsets = "";
|
|
var test3;
|
|
window.Charsetsize = ps.Charsetsize;
|
|
window.UseGRC = document.getElementById("grc");
|
|
window.Use33 = document.getElementById("33");
|
|
|
|
if (window.Use33.checked == true){
|
|
window.passwordStrength.Charsets = {
|
|
Number: "0123456789",
|
|
Lowercase: "abcdefghijklmnopqrstuvwxyz",
|
|
Uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
Symbol: " !#$%∞&'()*+,./:;<=>?@[]^_`{|}~-\""
|
|
};
|
|
window.Use33.checked = true;
|
|
} else {
|
|
window.passwordStrength.Charsets = {
|
|
Number: "0123456789",
|
|
Lowercase: "abcdefghijklmnopqrstuvwxyz",
|
|
Uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
Symbol: " !#$%&'()*+,./:;<=>?@[]^_`{|}~-\""
|
|
};
|
|
window.Use33.checked = false;
|
|
}
|
|
|
|
ShannonEntropyBits = 0;
|
|
TrigraphEntropyBits = 0;
|
|
PasswordLengthtext = "";
|
|
if (ps) {
|
|
Object.keys(ps).forEach(function (key) {
|
|
if (ps[key] && typeof ps[key] === "object") {
|
|
Charsets += "<br><b>" + key + ":" + "</b>";
|
|
Object.keys(ps[key]).forEach(function (innerKey) {
|
|
Charsets += "<br><b> " + innerKey + ": </b>" + ps[key][innerKey];
|
|
});
|
|
} else {
|
|
stats += key + ": " + ps[key];
|
|
}
|
|
|
|
window.ExactSearchSpaceSize = Math.pow(window.Charsetsize, ps.PasswordLength);
|
|
window.ESSS = Math.pow(window.Charsetsize, ps.PasswordLength);
|
|
|
|
function grc(len)
|
|
{
|
|
if(len < 1)
|
|
{
|
|
return 0 ;
|
|
}
|
|
if (len == 1)
|
|
{
|
|
return window.ESSS;
|
|
}
|
|
return Math.pow(window.Charsetsize, len - 1) + grc(len - 1);
|
|
}
|
|
|
|
window.GRCESSS = grc(ps.PasswordLength)
|
|
window.UseGRC = document.getElementById("grc");
|
|
|
|
if (window.UseGRC.checked == true){
|
|
window.ESSS = window.GRCESSS;
|
|
window.ExactSearchSpaceSize = window.GRCESSS;
|
|
}
|
|
|
|
function numberWithCommas(num) {
|
|
var num_parts = num.toString().split(".");
|
|
num_parts[0] = num_parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
return num_parts.join(".");
|
|
}
|
|
|
|
window.PossibleCombinations = numberWithCommas(window.ESSS)
|
|
|
|
function getTime(rate){
|
|
|
|
var s = window.ExactSearchSpaceSize/rate;
|
|
|
|
var decimalYears = s/(3600*24*365);
|
|
var years = Math.floor(decimalYears);
|
|
|
|
var decimalMonths =(decimalYears-years)*12;
|
|
var months = Math.floor(decimalMonths);
|
|
|
|
var decimalDays = (decimalMonths-months)*30;
|
|
var days = Math.floor(decimalDays);
|
|
|
|
var decimalHours = (decimalDays-days)*24;
|
|
var hours = Math.floor(decimalHours);
|
|
|
|
var decimalMinutes = (decimalHours-hours)*60;
|
|
var minutes = Math.floor(decimalMinutes);
|
|
|
|
var decimalSeconds = (decimalMinutes-minutes)*60;
|
|
var seconds = Math.floor(decimalSeconds);
|
|
|
|
var decimalMilliseconds = (decimalSeconds)*1000;
|
|
var milliseconds = Math.floor(decimalMilliseconds);
|
|
|
|
var decimalMicroseconds = (decimalMilliseconds)*1000;
|
|
var microseconds = Math.floor(decimalMicroseconds);
|
|
|
|
var decimalNanoseconds = (decimalMicroseconds)*1000;
|
|
var nanoseconds = Math.floor(decimalNanoseconds);
|
|
|
|
var time = [];
|
|
|
|
if(years > 0){
|
|
if(years == 1)
|
|
time.push("1 year, ");
|
|
else
|
|
time.push(numberWithCommas(years) + " years, ");
|
|
}
|
|
if(months > 0){
|
|
if(months == 1)
|
|
time.push("1 month, ");
|
|
else
|
|
time.push(months + " months, ");
|
|
}
|
|
if(days > 0){
|
|
if(days == 1)
|
|
time.push("1 day, ");
|
|
else
|
|
time.push(days + " days, ");
|
|
}
|
|
if(hours > 0){
|
|
if(hours == 1)
|
|
time.push("1 hour, ");
|
|
else
|
|
time.push(hours + " hours, ");
|
|
}
|
|
if(minutes > 0){
|
|
if(minutes == 1)
|
|
time.push("1 minute, ");
|
|
else
|
|
time.push(minutes + " minutes, ");
|
|
}
|
|
if(seconds > 0){
|
|
if(seconds == 1)
|
|
time.push("1 second, ");
|
|
else
|
|
time.push(seconds + " seconds, ");
|
|
window.seconds = seconds
|
|
}
|
|
|
|
if(milliseconds > 0){
|
|
if(milliseconds == 1)
|
|
time.push("1 Milisecond, ");
|
|
else
|
|
if(seconds > 1) {
|
|
var fixmilliseconds = (seconds * 1000) - milliseconds;
|
|
milliseconds = fixmilliseconds - (fixmilliseconds * 2);
|
|
time.push(milliseconds + " Milliseconds, ");
|
|
}else{
|
|
time.push(milliseconds + " Milliseconds, ");
|
|
}
|
|
|
|
}
|
|
|
|
if(microseconds > 0){
|
|
if(microseconds == 1)
|
|
time.push("1 Microsecond, ");
|
|
else
|
|
if(milliseconds > 1) {
|
|
var fixmicroseconds = (milliseconds * 1000) - microseconds;
|
|
microseconds = fixmicroseconds - (fixmicroseconds * 2);
|
|
time.push(microseconds + " Microseconds, ");
|
|
}else{
|
|
time.push(microseconds + " Microseconds, ");
|
|
}
|
|
}
|
|
|
|
if(nanoseconds > 0){
|
|
if(nanoseconds == 1)
|
|
time.push("1 Nanosecond, ");
|
|
else
|
|
if(microseconds > 1) {
|
|
var fixnanoseconds = (microseconds * 1000) - nanoseconds;
|
|
nanoseconds = fixnanoseconds - (fixnanoseconds * 2);
|
|
time.push(nanoseconds + " Nanoseconds, ");
|
|
}else{
|
|
time.push(nanoseconds + " Nanoseconds, ");
|
|
}
|
|
}
|
|
|
|
if(time.length <= 0)
|
|
time = "Less Then a Nanosecond, ";
|
|
else if(time.length == 1)
|
|
time = time[0];
|
|
else
|
|
time = time[0] + time[1];
|
|
|
|
return time.substring(0,time.length-2);
|
|
}
|
|
|
|
window.CustomScenarioNumber = getTime(document.getElementById('customscenario').value)
|
|
if(document.getElementById('customscenario').value == ""){
|
|
window.CustomScenarioNumber = "Guesses Per Second Not Specified."
|
|
}
|
|
window.OnlineAttackScenarioNumber = getTime(1000)
|
|
window.PasswordDepotsExampleScenarioNumber = getTime(2000000000)
|
|
window.OfflineFastAttackScenarioNumber = getTime(100000000000)
|
|
window.DistributedScenarioNumber = getTime(800000000000)
|
|
window.MassiveCrackingArrayScenarioNumber = getTime(100000000000000)
|
|
|
|
document.getElementById('length').innerHTML = '<td class="txt" style="color: black;">Password Length: ' + ps.PasswordLength + "</td>";
|
|
ShannonEntropyBits = "<br><b>" + "ShannonEntropyBits: </b>" + ps.ShannonEntropyBits.toFixed(2);
|
|
TrigraphEntropyBits = "<br><b>" + "TrigraphEntropyBits: </b>" + ps.TrigraphEntropyBits.toFixed(2);
|
|
var CustomScenario = "<br><b>" + "Custom Scenario: </b>" + window.CustomScenarioNumber;
|
|
var OnlineAttackScenario = "<br><b>" + "GRC.com Online Attack Example Scenario: </b>" + window.OnlineAttackScenarioNumber;
|
|
var PasswordDepotsExampleScenario = "<br><b>" + "Password-Depot.de Example Scenario: </b>" + window.PasswordDepotsExampleScenarioNumber;
|
|
var OfflineFastAttackScenario = "<br><b>" + "GRC.com Offline Fast Attack Example Scenario: </b>" + window.OfflineFastAttackScenarioNumber;
|
|
var DistributedScenario ="<br><b>" + "Distributed.net May 8, 2012 Scenario: </b>" + window.DistributedScenarioNumber;
|
|
var MassiveCrackingArrayScenario ="<br><b>" + "GRC.com Massive Cracking Array Example: </b>" + window.MassiveCrackingArrayScenarioNumber;
|
|
StrengthCode = "<br><b>" + "Strength Code: </b>" + ps.StrengthCode;
|
|
var tableStart = '<table id="info" cellpadding="0" cellspacing="0" class="r_box" align="center"><td>';
|
|
var tableEnd = '</td></table>';
|
|
var SearchTitle = "<b>" + "[Time Required to Exhaustively Search All Possible Combinations]"
|
|
var PC = "<br><b>" + "All Possible combinations: </b>" + window.PossibleCombinations;
|
|
|
|
if(ps.CommonPassword == true){
|
|
CommonPasswordvalue = "<br><b>" + "WARNING: </b>" + "<font color=black>[</font><font color=red>Common Password!</font><font color=black>]</font>";
|
|
StrengthCode = "<br><b>" + "Strength Code: </b>" + "<font color=red>Extremely Weak</font>";
|
|
}else{CommonPasswordvalue = ""}
|
|
|
|
if (ps.PasswordLength <= 4) {
|
|
PasswordLengthtext = "<br><b>" + "WARNING: </b>" + "<font color=black>[</font><font color=red>Very Short Password!</font><font color=black>]</font>";
|
|
}else if (ps.PasswordLength <= 7) {
|
|
PasswordLengthtext = "<br><b>" + "WARNING: </b>" + "<font color=black>[</font><font color=red>Short Password!</font><font color=black>]</font>";}
|
|
|
|
if (ps.TrigraphEntropyBits == "Infinity") {
|
|
TrigraphEntropyBits = "<br><b>" + "EntropyBits: </b>" + "∞";
|
|
ShannonEntropyBits = "";
|
|
AverageBits = "";
|
|
}
|
|
|
|
Charsetsize = "<b>" + "Charsetsize: </b>" + window.Charsetsize;
|
|
if (ps.Charsets.Uppercase == true){document.getElementById('upper').innerHTML = '<td class="txt" style="color: green;">Uppercase</td>'}else{document.getElementById('upper').innerHTML = '<td class="txt" style="color: black;">Uppercase</td>'}
|
|
if (ps.Charsets.Lowercase == true){document.getElementById('lower').innerHTML = '<td class="txt" style="color: green;">Lowercase</td>'}else{document.getElementById('lower').innerHTML = '<td class="txt" style="color: black;">Lowercase</td>'}
|
|
if (ps.Charsets.Number == true){document.getElementById('number').innerHTML = '<td class="txt" style="color: green;">Number</td>'}else{document.getElementById('number').innerHTML = '<td class="txt" style="color: black;">Number</td>'}
|
|
if (ps.Charsets.Symbol == true){document.getElementById('symbol').innerHTML = '<td class="txt" style="color: green;">Symbol</td>'}else{document.getElementById('symbol').innerHTML = '<td class="txt" style="color: black;">Symbol</td>'}
|
|
|
|
stats = Charsetsize + ShannonEntropyBits + TrigraphEntropyBits + CommonPasswordvalue + PasswordLengthtext + StrengthCode + PC + tableStart + SearchTitle + CustomScenario + OnlineAttackScenario + PasswordDepotsExampleScenario + OfflineFastAttackScenario + DistributedScenario + MassiveCrackingArrayScenario + tableEnd;
|
|
});
|
|
}
|
|
if (ps.PasswordLength <= 0) {
|
|
passwordStatistics.innerHTML = "Enter a password to see its strength.";
|
|
}else{passwordStatistics.innerHTML = stats;
|
|
}
|
|
}
|
|
|
|
function setReadyStateChange(xhr, passwordStrengthMethod, elementId) {
|
|
xhr.onreadystatechange = function () {
|
|
var result;
|
|
|
|
if (xhr.readyState == 4) {
|
|
if (xhr.status == 200) {
|
|
passwordStrength[passwordStrengthMethod](JSON.parse(xhr.responseText));
|
|
document.getElementById(elementId).style.display = 'none';
|
|
recalc();
|
|
} else {
|
|
document.getElementById(elementId).innerHTML = "Unable to load (" + elementId + "): " + xhr.status;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
passwordField = document.getElementById('PasswordField');
|
|
var recalculatebutton = document.getElementById('recalculate');
|
|
var use33checkbox = document.getElementById('33');
|
|
var UseGRCbox = document.getElementById("grc");
|
|
CustomScenarioField = document.getElementById('customscenario');
|
|
passwordInfo = document.getElementById('PasswordInfo');
|
|
passwordStatistics = document.getElementById('PasswordStatistics');
|
|
|
|
commonXhttp = new XMLHttpRequest();
|
|
setReadyStateChange(commonXhttp, 'addCommonPasswords', 'commonXhttp');
|
|
commonXhttp.open("GET", "https://raw.githubusercontent.com/tests-always-included/password-strength/master/data/common-passwords.json", true);
|
|
commonXhttp.send();
|
|
|
|
frequencyXhttp = new XMLHttpRequest();
|
|
setReadyStateChange(frequencyXhttp, 'addTrigraphMap', 'frequencyXhttp');
|
|
frequencyXhttp.open("GET", "https://raw.githubusercontent.com/tests-always-included/password-strength/master/data/trigraphs.json", true);
|
|
frequencyXhttp.send();
|
|
|
|
function wait(ms){
|
|
var start = new Date().getTime();
|
|
var end = start;
|
|
while(end < start + ms) {
|
|
end = new Date().getTime();
|
|
}
|
|
}
|
|
|
|
passwordStrength = new window.PasswordStrength();
|
|
passwordField.onkeyup = recalc;
|
|
CustomScenarioField.onkeyup = recalc;
|
|
recalculatebutton.onclick = recalc;
|
|
window.recalc = recalc;
|
|
|
|
|
|
|
|
// Expose this object so the console can diagnose and debug
|
|
// issues.
|
|
window.passwordStrength = passwordStrength;
|
|
};
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<!-- HEADER -->
|
|
<div id="header_wrap" class="outer">
|
|
<header class="inner">
|
|
|
|
<h1 id="project_title">Password Entropy Calculator</h1>
|
|
|
|
</header>
|
|
</div>
|
|
|
|
<!-- MAIN CONTENT -->
|
|
<div id="main_content_wrap" class="outer">
|
|
<section id="main_content" class="inner">
|
|
|
|
<p id="commonXhttp" style="display: none;">Loading common words ...</p>
|
|
<p id="frequencyXhttp" style="display: none;">Loading frequency table ...</p>
|
|
|
|
<p>Calculates the relative strength of a password. This is accomplished by using several techniques. Primarily this relies on letter trigraphs, which check each set of 3 characters in a given password. This also calculates the entropy bits based on Claude Shannon's technique on determining the number of bits required to represent a set of characters and multiplying it by the length of the password. There is also a check to see if a password is contained in a list of common passwords.<p>
|
|
<p>As a bonus there's also a "Search Space" or (Total Possible Combinations) Calculator, inspired by <a href="https://www.grc.com/haystack.htm" target="_blank">GRC's Interactive Brute Force Password “Search Space” Calculator.</a></p>
|
|
<p>Try some of the calculations from Password.Depot.de if you'd like.</p>
|
|
<p>Side note: Password.Depot.de's calculation for 94^9 is incorrect. Google it lol</p>
|
|
Custom Scenario = <input id="customscenario" type="number" autocomplete="off" autofocus="autofocus" style="background-color: white; width: 20%; cursor: auto;"> Guesses Per Second.
|
|
<p>GRC's Online Attack Example Scenario = 1 Thousand Guesses Per Second.</p>
|
|
<p><a href="https://www.password-depot.de/en/know-how/brute-force-attacks.htm" target="_blank">Password-Depot.de</a> Example Scenario = 2 Billion Guesses Per Second.</p>
|
|
<p>GRC's Offline Fast Attack Example Scenario = 100 Billion Guesses Per Second.</p>
|
|
<p><a href="https://www.distributed.net/Main_Page" target="_blank">Distributed.net</a> May 8, 2012 Scenario = 800 Billion Guesses Per Second.</p>
|
|
<p>GRC's Massive Cracking Array Example Scenario = 100 Trillion Guesses Per Second.</p>
|
|
<div id="PasswordInfo">
|
|
Type your password in here:
|
|
<form _lpchecked="1">
|
|
<input id="PasswordField" type="password" autocomplete="off" autofocus="autofocus" style="background-color: white; width: 66%; margin-left: -10px; cursor: auto;"> <input id="recalculate" type="button" value="Recalculate">
|
|
</form>
|
|
<input id="grc" type="checkbox">Use GRC's Algorithm.
|
|
<input id="33" type="checkbox">Use 33 Symbol Charset (GRC Uses This).
|
|
<table id="charset">
|
|
<tr>
|
|
<td id="charset"><table id="info" border="0" cellpadding="0" cellspacing="0"><tbody><tr id="upper"><td class="txt" style="color: black;">Uppercase</td></tr></tbody></table></td>
|
|
<td id="charset"><table id="info" border="0" cellpadding="0" cellspacing="0"><tbody><tr id="lower"><td class="txt" style="color: black;">Lowercase</td></tr></tbody></table></td>
|
|
<td id="charset"><table id="info" border="0" cellpadding="0" cellspacing="0"><tbody><tr id="number"><td class="txt" style="color: black;">Numbers</td></tr></tbody></table></td>
|
|
<td id="charset"><table id="info" border="0" cellpadding="0" cellspacing="0"><tbody><tr id="symbol"><td class="txt" style="color: black;">Symbols</td></tr></tbody> </table></td>
|
|
<td id="charset"><table id="info" border="0" cellpadding="0" cellspacing="0"><tbody><tr id="length"><td class="txt" style="color: black;">Password Length: 0</td></tr></tbody> </table></td>
|
|
</tr>
|
|
</table>
|
|
<table id="info" border="0" cellpadding="0" cellspacing="0" class="r_box" align="center" style="margin-left: 10px; margin-top: 0px; margin-bottom: 0px; width: 95%;"><tbody><tr><td class="r_box"><span id="PasswordStatistics"></span></td></tr></tbody></table>
|
|
</body></html>
|