Initial commit

This commit is contained in:
2019-10-04 05:52:54 +02:00
commit 9a70f88a3e
4 changed files with 285 additions and 0 deletions

207
ewpass.js Normal file
View File

@@ -0,0 +1,207 @@
var ewpass = {};
(function(ns) {
const SEED_PREFIX = 'E. W. Password Generator Seed/';
const DERIVE_PREFIX = 'Domain Password/';
const Storage = window.localStorage;
const BASIC_MAP = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const EXTENDED_MAP = BASIC_MAP + '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?';
var settings = {
'checksum': undefined,
'DEFAULT': [ 16, 1, true ],
'domains': {}
};
var currentDomain = undefined;
var seedValid = false;
var checkSeedChecksum = () => {
const valid = $('#checksum').text() === settings.checksum;
$('#chkok').html(valid ? '✓' : '✗');
$('#chkset').prop('disabled', valid ? 'disabled' : '');
seedValid = valid;
};
var setDomains = () => {
$domainlist = $("#domainlist")
$domainlist.html('');
for (domain in settings.domains) {
$domainlist.append($('<option>').attr('value', domain).text(domain));
}
};
var calcRounds = l => {
var m = 0xffffffff;
var n = 0;
while (m > 0) {
m = Math.floor(m / l);
n++;
}
return n;
};
ns.resetSeed = () => {
$('#seed').val('');
$('#seed').prop('disabled', '');
$('#checksum').text('-');
$('#chkok').text('');
$('#chkset').prop('disabled', 'disabled');
seedValid = false;
$('#seed').focus();
};
ns.calculateSeed = () => {
sha256(SEED_PREFIX + $('#seed').val()).then(hash => {
$('#seed').val(hash);
$('#seed').prop('disabled', 'disabled');
$('#checksum').text(hash.substring(0, 2));
checkSeedChecksum();
});
};
ns.storeSeedChecksum = () => {
settings.checksum = $('#checksum').text();
Storage.setItem('checksum', settings.checksum);
checkSeedChecksum();
};
ns.checkDomainInputMatchesToOption = () => {
var domain = $('#domain').val();
if (domain in settings.domains) {
ns.loadSettingsForDomain(domain);
}
};
ns.loadSettingsForDomain = (domain) => {
if (domain === undefined) {
ns.loadSettingsForDomain($('#domain').val());
return;
}
var _settings;
if (domain === '') {
$('#length').prop('disabled', 'disabled');
$('#iteration').prop('disabled', 'disabled');
$('#nextiter').prop('disabled', 'disabled');
$('#extended').prop('disabled', 'disabled');
$('#storedomain').prop('disabled', 'disabled');
$('#docopy').prop('disabled', 'disabled');
_settings = settings.DEFAULT;
} else if (domain in settings.domains) {
_settings = settings.domains[domain];
} else if (!confirm('Add new domain "' + domain + '" ?')) {
$('#domain').val('');
ns.loadSettingsForDomain('');
return;
} else {
settings.domains[domain] = settings.DEFAULT.slice();
setDomains();
_settings = settings.domains[domain];
Storage.setItem('domains', JSON.stringify(settings.domains));
}
$('#length').val(_settings[0]);
$('#iteration').val(_settings[1]);
$('#extended').prop('checked', _settings[2] ? 'checked' : '');
if (_settings !== settings.DEFAULT) {
$('#length').prop('disabled', '');
$('#iteration').prop('disabled', '');
$('#nextiter').prop('disabled', '');
$('#extended').prop('disabled', '');
$('#storedomain').prop('disabled', '');
$('#docopy').prop('disabled', '');
currentDomain = domain;
}
};
ns.storeSettingsForDomain = () => {
_settings = [
parseInt($('#length').val()),
parseInt($('#iteration').val()),
$('#extended').prop('checked')
];
settings.domains[currentDomain] = _settings;
Storage.setItem('domains', JSON.stringify(settings.domains));
};
ns.nextIteration = () => {
$('#iteration').val(parseInt($('#iteration').val()) + 1);
ns.storeSettingsForDomain();
};
ns.deriveAndCopyToClipboard = () => {
if (!seedValid) {
alert('Enter master password first!');
ns.resetSeed();
return;
}
$docopy = $('#docopy');
$docopy.prop('disabled', 'disabled');
sha256(DERIVE_PREFIX + $('#seed').val() + '/' + settings.domains[currentDomain][1] + '/' + currentDomain).then(hash => {
const map = settings.domains[currentDomain][2] ? EXTENDED_MAP : BASIC_MAP;
const rounds = calcRounds(map.length);
var pw = '';
for (var i = 0; i < 8; i++) {
var x = parseInt(hash.substring(i * 8, (i+1) * 8), 16);
for (var j = 0; j < rounds; j++) {
pw += map.substring(x % map.length, x % map.length + 1);
x = Math.floor(x / map.length);
}
}
pw = pw.substring(0, settings.domains[currentDomain][0]);
navigator.clipboard.writeText(pw).then(() => {
$docopy.val('[ COPIED ]');
setInterval(() => {
$docopy.val('Copy password');
$docopy.prop('disabled', '');
}, 1500);
});
});
};
ns.setup = () => {
if (Storage.getItem('checksum') != null) {
settings.checksum = Storage.getItem('checksum');
}
if (Storage.getItem('domains') != null) {
settings.domains = JSON.parse(Storage.getItem('domains'));
console.debug('Loaded domains:', settings.domains);
setDomains();
}
var setupReturnEvent = (o, handler) => {
o.keypress((event, value) => {
if (event.which === 13) {
handler(o.val());
}
});
};
setupReturnEvent($('#seed'), ns.calculateSeed);
setupReturnEvent($('#domain'), ns.loadSettingsForDomain);
ns.resetSeed();
ns.loadSettingsForDomain();
};
})(ewpass);

72
index.html Normal file
View File

@@ -0,0 +1,72 @@
<html>
<head>
<script language="javascript" src="./jquery-3.4.1.min.js"></script>
<script language="javascript" src="./sha256.js"></script>
<script language="javascript" src="./ewpass.js"></script>
</head>
<body onload="ewpass.setup();">
<center>
<table border="0" cellspacing="10">
<tr>
<th>Master password: </th>
<td>
<table border="0" cellspacing="5">
<tr>
<td colspan="3">
<input type="password" id="seed" size="46" autocomplete="off" /> <input type="button" id="clearseed" value="Clear" onclick="ewpass.resetSeed();" />
</td>
</tr>
<tr>
<td>Checksum: </td>
<td><span id="chkok"></span> <span id="checksum"></span></td>
<td><input type="button" id="chkset" value="Set" onclick="ewpass.storeSeedChecksum();" /></td>
</tr>
</table>
</td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
<tr>
<th>Domain: </th>
<td><input id="domain" list="domainlist" autocomplete="off" onchange="ewpass.checkDomainInputMatchesToOption();" /><datalist id="domainlist"></datalist></td>
</tr>
<tr>
<th>Options: </th>
<td>
<table border="0" cellspacing="2">
<tr>
<td>Length: </td>
<td><input id="length" value="12" size="2" autocomplete="off" /></td>
</tr>
<tr>
<td>Iteration: </td>
<td><input id="iteration" value="1" size="2" autocomplete="off" /> <input type="button" id="nextiter" value="Next" onclick="ewpass.nextIteration();" /></td>
</tr>
<tr>
<td colspan="2">
<label><input type="checkbox" id="extended" checked="checked" /> Use special characters</label>
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" id="storedomain" value="Store" onclick="ewpass.storeSettingsForDomain();" />
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
<tr>
<td colspan="2">
<center>
<input type="button" id="docopy" value="Copy password" onclick="ewpass.deriveAndCopyToClipboard();" />
</center>
</td>
</tr>
</table>
</center>
</body>
</html>

2
jquery-3.4.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
sha256.js Normal file
View File

@@ -0,0 +1,4 @@
async function sha256(message) {
const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(message));
return Array.from(new Uint8Array(hashBuffer)).map(b => ('00' + b.toString(16)).slice(-2)).join('');
}