Skip to content

Instantly share code, notes, and snippets.

@earonesty
Created December 27, 2016 15:30
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 earonesty/b45e675cb5e0b2263b86023644a98575 to your computer and use it in GitHub Desktop.
Save earonesty/b45e675cb5e0b2263b86023644a98575 to your computer and use it in GitHub Desktop.
<html>
<!-- (c) Copyright 2013, Erik Aronesty, All Rights Reserved (erik at q32.com) -->
<head>
<link rel="shortcut icon" href="/favicon.ico" />
<script src="/pouch.js"></script>
<script language="JavaScript">
var pdb = new PouchDB("passhash");
if (pdb) {
// replicate site info... for everyone... yay
pdb.replicate.to("https://hereausecingedurporrower:G1O17O2PRBbok6QDRk8Ie7HQ@erik.cloudant.com/passhash", {continuous : true});
pdb.replicate.from("https://hereausecingedurporrower:G1O17O2PRBbok6QDRk8Ie7HQ@erik.cloudant.com/passhash", {continuous : true});
}
var userKeyL="";
var siteKeyL="";
var siteKeyG="";
var siteDefault={size: 24, nonce: "", notes: "", nospecial: 0, nodigit: 0};
var siteInfo=siteDefault;
var userDefault={verif: ""};
var userInfo=userDefault;
var prevSite;
function selectAll(id)
{
document.getElementById(id).focus();
document.getElementById(id).select();
}
function userChange() {
webChange();
var sha = CryptoJS.SHA512(username.value);
userKey = sha.toString(CryptoJS.enc.Hex).substr(0,64);
getDatL(userKey, userDefault, function(v) {userInfo=v});
}
function webChange() {
var sha = CryptoJS.SHA512(username.value + ':' + site.value);
// old version
siteKeyL = sha.toString(CryptoJS.enc.Hex).substr(0,64);
// new version
siteKeyG = sha.toString(CryptoJS.enc.Hex);
if (!prevSite || (prevSite != siteKeyL)) {
getDatG(siteKeyG, siteKeyL, siteDefault, function(info) {
siteInfo=info;
if (siteInfo.notes) {
notes.value=siteInfo.notes;
} else {
notes.value="";
}
nonce.value = siteInfo.nonce ? siteInfo.nonce : "";
nospecial.checked=!!siteInfo.nospecial;
nodigit.checked=!!siteInfo.nodigit;
// some websites restrict password length
var has_short20=new RegExp('paypal|alibaba|serve.com|aliexpress','i');
has_short20=has_short20.exec(site.value);
if (has_short20) {
if (siteInfo.size>20) {
siteInfo.size=20;
}
}
// some websites restrict password length stupidly
var has_short15=new RegExp('starbucks','i');
has_short15=has_short15.exec(site.value);
if (has_short15) {
if (siteInfo.size>15) {
siteInfo.size=15;
}
}
// some websites restrict password length stupidly
var has_short12=new RegExp('homedepot','i');
has_short12=has_short12.exec(site.value);
if (has_short12) {
if (siteInfo.size>12) {
siteInfo.size=12;
}
}
var has_nospecial=new RegExp('alibaba|aliexpress|serve.com','i');
has_nospecial=has_nospecial.exec(site.value);
nospecial.checked=has_nospecial;
size.value=siteInfo.size;
sizeChange();
prevSite=siteKeyL;
});
}
}
function sizeChange() {
if (size.value>=8) {
siteInfo.size=size.value;
min8.display="none";
} else {
siteInfo.size=24;
min8.display="block";
}
if (settings.style.display!="block") {
sizebutton.innerHTML=siteInfo.size;
}
}
function clearHash() {
// this happens at a timeout
hash.value="";
password.value="";
output.style.display="none";
}
function doHash()
{
// this is safe enough to save - most recent user
setDatX("user",username.value);
// load site info
webChange();
var shaX = CryptoJS.SHA512(username.value + ':' + password.value + nonce.value);
var verX = shaX.toString(CryptoJS.enc.Base64);
var askSave=0;
// if we have previously saved info...
if (userInfo.verif) {
// same user... different password?
if (userInfo.verif!=verX) {
askSave=1;
showWarn();
} else {
askSave=0;
hideWarn();
}
}
// save prev value
if (!askSave) {
userInfo.verif=verX;
setDatL(userKeyL,userInfo);
}
siteInfo.notes=notes.value;
siteInfo.nonce=nonce.value;
setDatG(siteKeyG,siteKeyL,siteInfo);
// hash it
var sha = CryptoJS.SHA512(username.value + ':' + password.value + ':' + site.value);
var res = sha.toString(CryptoJS.enc.Base64);
// change size
hash.value=res.substr(0,siteInfo.size);
if (nospecial.checked) {
hash.value=hash.value.replace(/[\/+=!]/g,'');
}
if (nodigit.checked) {
hash.value=hash.value.replace(/[0-9]/g,'');
}
// check for annoying stuff
var has_dig=new RegExp('[0-9]','');
var has_punc=new RegExp('[/+=]','');
var has_cap=new RegExp('[A-Z]','');
var has_low=new RegExp('[a-z]','');
has_dig=has_dig.exec(hash.value);
has_punc=has_punc.exec(hash.value);
has_cap=has_cap.exec(hash.value);
has_low=has_low.exec(hash.value);
// append stuff that does nothing for security, but makes some sites happy
if (!has_dig && !nodigit.checked) { hash.value = hash.value + '1'; }
if (!has_punc && !nospecial.checked) { hash.value = hash.value + '!'; }
if (!has_cap) { hash.value = hash.value + 'A'; }
if (!has_low) { hash.value = hash.value + 'a'; }
if (hash.value.length > siteInfo.size) {
// restrict to limited size
hash.value=res.substr(0,siteInfo.size-(hash.value.length-siteInfo.size)) + hash.value.substr(siteInfo.size);
}
output.style.display="block";
hash.select();
// clear window, in case the user forgets to close it
window.setInterval(clearHash,1000*60*60*24);
return false;
}
function onLoad() {
username.value=getDatX("user");
userChange();
// select the first empty field
if (username.value != "") {
if (password.value != "") {
site.select();
} else {
password.select();
}
} else {
username.select();
};
}
function setDatX(nameL, value) {
if(typeof(Storage)!=="undefined") {
localStorage.setItem(nameL, value);
}
else {
// Sorry! No web storage support..
setCookie(nameL, 100);
}
}
function setDatL(nameL, value) {
setDatX(nameL,JSON.stringify(value))
}
function setDatG(nameG, nameL, value) {
if (pdb) {
value._id=nameG;
// pouch supported? that's all we need
pdb.get(nameG, function(err, resp) {
if (resp) {
value._rev=resp._rev;
pdb.put(value);
} else {
pdb.put(value);
}
}, function () {
pdb.put(value);
});
} else {
setDatL(nameL, value);
}
}
// global get
function getDatG(nameG, nameL, defV, func) {
if (pdb) {
pdb.get(nameG, function(err, resp) {
if (resp) {
func(resp);
} else {
// support old values, if any
return getDatL(nameL, defV, func);
}
}, function () {
// support old values, if any
return getDatL(nameL, defV, func);
});
} else {
return getDatL(nameL, defV, func);
}
}
function getDatL(nameL, defV, func) {
var x= getDatX(nameL);
if (x)
x=JSON.parse(x);
if (x)
return(x);
// make a copy of default value
func(JSON.parse(JSON.stringify(defV)));
}
// local get
function getDatX(nameL) {
if(typeof(Storage)!=="undefined") {
return localStorage.getItem(nameL);
}
else {
// Sorry! No web storage support..
return getCookie(nameL);
}
}
// generic functions
function setCookie(c_name,value,exdays)
{
var exdate=new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
document.cookie=c_name + "=" + c_value;
}
function getCookie(c_name)
{
var c_value = document.cookie;
var c_start = c_value.indexOf(" " + c_name + "=");
if (c_start == -1)
{
c_start = c_value.indexOf(c_name + "=");
}
if (c_start == -1)
{
c_value = null;
}
else
{
c_start = c_value.indexOf("=", c_start) + 1;
var c_end = c_value.indexOf(";", c_start);
if (c_end == -1)
{
c_end = c_value.length;
}
c_value = unescape(c_value.substring(c_start,c_end));
}
return c_value;
}
function toggleSettings() {
if (settings.style.display=="block") {
settings.style.display="none";
sizebutton.innerHTML=siteInfo.size;
sizebutton.title="Size of generated password, click to change";
} else {
settings.style.display="block";
sizebutton.innerHTML="&uarr;";
size.value=siteInfo.size;
sizebutton.title="Close settings";
}
}
function showNote() {
note.style.display="block";
}
function hideNote() {
note.style.display="none";
}
function showWarn() {
warn.style.display="block";
}
function hideWarn() {
warn.style.display="none";
}
function saveMaster() {
var shaX = CryptoJS.SHA512(username.value + ':' + password.value);
var verX = shaX.toString(CryptoJS.enc.Base64);
userInfo.verif=verX;
setDatL(userKeyL,userInfo);
hideWarn();
}
</script>
<!-- hash and encoding functions -->
<script src="sha512.js"></script>
<script src="enc-base64-min.js"></script>
<!-- b&w style, should be a param -->
<style>
body,p,h1,h2,h3,h4,table,td,th,ul,ol,textarea,input {
font-family:verdana,helvetica,arial,sans-serif;
}
label { display: inline-block; width: 5.5em; text-align: right; }
input { border-radius: 7px; }
h3 { background-color: black; color: white; border-radius: 10px; padding-top:.25em; padding-bottom:.25em; white-space:nowrap; }
#frame { border-radius: 10px; border: 5px solid gray; padding: 0.5em; width: 22em; text-align: center; margin-left: 20px; margin-top: 20px;}
#inner { text-align: left; }
#note { font-size: small; color: gray; }
#warn { font-size: small; color: red; }
#sizebutton {
-moz-box-shadow:inset 0px 1px 2px 0px #ffffff;
-webkit-box-shadow:inset 0px 1px 2px 0px #ffffff;
box-shadow:inset 0px 1px 2px 0px #ffffff;
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf) );
background:-moz-linear-gradient( center top, #ededed 5%, #dfdfdf 100% );
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf');
background-color:#ededed;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
text-indent:0;
border:1px solid #bbb;
display:inline-block;
color:#777;
font-family:Arial;
font-size:.85em;
font-weight:normal;
font-style:normal;
height:1.4em;
text-decoration:none;
text-align:center;
padding-top:.2em;
padding-left:.4em;
padding-right:.4em;
vertical-align:center;
text-shadow:1px 1px 0px #fff;
}
#sizebutton:hover {
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #dfdfdf), color-stop(1, #ededed) );
background:-moz-linear-gradient( center top, #dfdfdf 5%, #ededed 100% );
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf', endColorstr='#ededed');
background-color:#dfdfdf;
}
#sizebutton:active {
position:relative;
top:1px;
}
</style>
</head>
<body onload="onLoad()">
<div id="frame">
<!-- stuff in this frame are centered -->
<!-- this should have an href to a better description, and info on how to embed, etc, -->
<h3><a onmouseover="showNote()" onmouseout="hideNote()"><span id=secure>Secure</span> Password Generator</a></h3>
<div id="inner">
<!-- stuff in this frame are aligned left -->
<p />
<!-- this form goes nowhere, but just in case set the action -->
<form name="login" id="login" action="javascript:false" autocomplete="on" onSubmit="return doHash(this);">
<label for=username>Username:</label> <input type="text" name="username" title="This is your username, so more than one identity can use passhash on this computer." onChange="userChange();" id="username" size="27" autocomplete="on" /><p />
<label for="password" title="This is your one, master password that nobody needs to know">Password:</label> <input type="password" name="password" id="password" size="27" autocomplete="on" /><p />
<label for="site">Website:</label> <input type=text onChange="webChange();" onClick="selectAll('site');" name="site" size="23" autocomplete="on" id="site"><span onclick="return toggleSettings();" id=sizebutton title="Size of generated password, click to change">24</span><p />
<div style="display:none;" id="settings">
<label for=size>Size:</label> <input type=text onChange="sizeChange()" onClick="selectAll('size');" name="size" size="10" autocomplete="on" id="size" /><p />
<label></label><input type=checkbox name="nospecial" autocomplete="on" id="nospecial" /> No Special
<input type=checkbox name="nodigit" autocomplete="on" id="nodigit" /> No Digit <p />
<label for=nonce title="If you change this, then the system will generate a different password.">Nonce:</label> <input type=text onClick="selectAll('nonce');" name="notes" size="27" autocomplete="on" id="nonce" /><p />
<label for=notes>Notes:</label> <input type=text onClick="selectAll('notes');" name="notes" size="27" autocomplete="on" id="notes" /><p />
</div>
<!-- empty lable, so the button lines up with other inputs -->
<label></label><input type="submit" value="Generate Password">
</form>
<p />
<textarea id="holdtext" style="display:none;"></textarea>
<form id="output" style="display:none;">
<label for="hash">Hash:</label> <input type=text id=hash name=hash value="" size=27 readonly style="background-color:pink;">
</form>
<p />
<!-- shown when user hovers over the title -->
<span id="note" style="display:none">Passwords are generated by Javascript, are never stored anywhere or transmitted anywhere. </span>
<!-- shown when user verification different than last time -->
<div id="warn" style="display:none">You master password is different than last time. <a href="javascript:saveMaster()" title="This is done without storing your master password, just a hash is stored in your browser">Click to remember this one instead.</a></div>
<!-- shown when user verification different than last time -->
<span id="min8" style="display:none">Minimum size is 8 characters</span>
<!-- shown when website/username/password are empty -->
<span id="help" style="display:none">Please enter a username, password and website. A password will then be made for that site.</span>
</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment