2
0

Reorganize core source and add Docker support to CLI.

This commit is contained in:
Maarten Billemont
2018-06-05 20:01:46 -04:00
parent 8e41cba7ac
commit c2aafd8602
215 changed files with 65 additions and 47 deletions

1
platform-independent/web/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
js/es5

View File

@@ -0,0 +1,112 @@
@import url(//fonts.googleapis.com/css?family=Flamenco:300|Exo+2:400,100,900);
/**** BASE STYLE ****/
html {
background: radial-gradient(black 15%, transparent 16%) 0 0,
radial-gradient(black 15%, transparent 16%) 8px 8px,
radial-gradient(rgba(255,255,255,.05) 15%, transparent 20%) 0 1px,
radial-gradient(rgba(255,255,255,.05) 15%, transparent 20%) 8px 9px;
background-color: #161616;
background-size: 16px 16px;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
body {
background: radial-gradient(transparent 16%, black);
width: 100%;
min-height: 100%;
padding: 0;
margin: 0;
clear: both;
font-family: 'Exo 2',
"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica,
Arial, "Lucida Grande", sans-serif;
font-weight: 300;
color: #DDD;
text-align: center;
}
h1, h2, h3, h4 {
margin: 0;
}
h1 {
font-size: 5em;
}
a {
font: inherit;
color: inherit;
}
input, select {
background: rgba(0, 0, 0, .3);
border: 1px solid #000;
border-radius: 4px;
width: 67%;
height: 2em;
margin: 1ex 0;
font: inherit;
color: inherit;
text-align: center;
}
input:focus, select:focus {
outline: none;
box-shadow: 0 0 50px #333;
}
input.half, select.half {
width: 33%;
}
input.minimal, select.minimal {
width: auto;
}
input[type="submit"], input[type="image"] {
background: transparent;
border: none;
width: auto;
font-size: 2em;
}
input[type="submit"] {
display: inline;
}
#identity.working input[type="submit"] {
display: none;
}
input[type="image"] {
display: none;
}
#identity.working input[type="image"] {
display: inline;
}
#error {
color: red;
}
header, section, footer {
overflow: hidden;
clear: both;
}
header, footer {
font-size: 0.8em;
}
section {
font-size: 2.5em;
}
/**** LAYOUT ****/
section {
display: none;
}
section.active {
display: block;
}
.small {
font-size: 0.5em;
}

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="white">
<path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/>
<path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z">
<animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite" />
</path>
</svg>

After

Width:  |  Height:  |  Size: 423 B

View File

@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Master Password &mdash; Secure your life, forget your passwords.</title>
<meta content="Master Password is an ingenious password solution that makes your passwords truly impossible to lose." />
<meta name="apple-itunes-app" content="app-id=510296984" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/main.css?2">
</head>
<body>
<header>
<p><strong>BETA</strong> - This site may not yet work on your browser.<br>
Passwords are generated locally, your master password is not sent to any server. See <a href="https://github.com/Lyndir/MasterPassword/tree/master/platform-independent/web-js">the source</a>.</p>
<span id="error"></span>
</header>
<section id="identity" class="active"><form action="#">
<h1>Identity</h1>
<p>
<input id="userName" type="text" placeholder="Your Full Name" /><br>
<input id="masterPassword" type="password" placeholder="Your Master Password" /><br>
<input type="submit" value="⏎" /><br>
<input type="image" src="img/spinner.svg" />
<select id="version" class="minimal">
<option value="0">V0</option>
<option value="1">V1</option>
<option value="2">V2</option>
<option value="3" selected>V3</option>
</select>
</p>
</form></section>
<section id="site">
<h1>Site</h1>
<p>
<input id="siteName" type="text" placeholder="Site Name (eg. apple.com)" /><br>
<span class="small">
<input id="siteCounter" type="number" placeholder="Site Counter" min="1" max="100" value="1" class="half" />
<select id="siteType" class="half">
<option value="pin">PIN</option>
<option value="short">Short</option>
<option value="basic">Basic</option>
<option value="medium">Medium</option>
<option value="long" selected>Long</option>
<option value="maximum">Maximum</option>
<option value="name">Name</option>
<option value="phrase">Phrase</option>
</select>
</span><br>
<h2 id="sitePassword" title="Your password is ..."></h2><br>
<input id="logout" type="submit" value="⎋" />
<input type="image" src="img/spinner.svg" />
</p>
</section>
<footer>
<p>This page was made possible thanks to <a href="https://tomthorogood.co.uk/" title="Tom Thorogood">tmthrgd</a>'s <a href="https://github.com/tmthrgd/mpw-js">mpw-js</a>.<br>
<em><a href="https://masterpassword.app">Master Password</a> is a security product and algorithm by <a href="http://www.lhunath.com" title="Maarten Billemont">Maarten Billemont</a>, <a href="http://www.lyndir.com">Lyndir</a> (&copy; 2011-2014).</em><br>
Usage implies agreement with our <a href="https://masterpassword.app/privacy.html">privacy policy and disclaimer</a>.</p>
</footer>
<!-- Scripts -->
<script>document.write('<script src="js/vendor/jquery-2.1.1.min.js"><\/script>')</script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-2.1.1.js"><\/script>')</script>
<script src="js/dependencies.js?4"></script>
<script src="js/main.js?3"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
build: mkdir-es5 es5/mpw-js/pbkdf2.js es5/mpw-js/scrypt.js es5/mpw-js/mpw.js es5/setImmediate-polyfill.js
mkdir-es5:
mkdir -p es5
mkdir -p es5/mpw-js
es5/%: $*
babel $* -o $@ --presets es2015 --source-maps
clean:
rm -rf es5 2>/dev/null
update:
git submodule update --init --recursive
git submodule foreach git pull origin master

View File

@@ -0,0 +1,8 @@
ES5
===
If your browser does not support ECMAScript 6, you will need to run babel to compile an ES5-compatible version of the ES6 mpw-js code.
1. Install npm. On OS X, you can use `brew install npm`.
2. Install babel. With npm installed, you can use `npm -g install babel-cli babel-preset-es2015`
3. Build the ES5-translation of the ES6 code. From this directory, just run `make`.

View File

@@ -0,0 +1,49 @@
// Test for required ES6 features
// Use an eval call to avoid a hard-fail on ES5 parsers.
var ES6 = false;
var esdir = "es5/";
try {
// Use ES6 code if the ES6 class, let, destructive assignment and rest arguments are supported.
eval("class $ES6 { constructor() { let b = true; this.b = b; } } var [ES6, esdir] = ((...args) => args)(new $ES6().b, '')");
} catch (e) {
}
// If crypto.subtle is not supported, try crypto.webkitSubtle instead.
if (window.crypto && !window.crypto.subtle && window.crypto.webkitSubtle)
window.crypto.subtle = window.crypto.webkitSubtle;
if ((!window.crypto || !window.crypto.subtle) && window.SubtleCrypto)
window.crypto = { subtle: window.SubtleCrypto }
// If Web Crypto API is not supported we include a JS crypto library
// https://code.google.com/p/crypto-js/
/* Disabled by default. Enable manually if your browser requires this.
if (!window.crypto || !window.crypto.subtle) {
document.write("<script src=https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js><\/script>");
document.write("<script src=https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js><\/script>");
document.write("<script src=https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/lib-typedarrays-min.js><\/script>");
}/**/
if (!Number.MAX_SAFE_INTEGER)
Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
// If Typed Arrays are not supported we include the polyfill
// https://github.com/inexorabletash/polyfill
window.ArrayBuffer || document.write("<script src=js/typedarray-polyfill.js><\/script>");
// If TextEncoder is not supported we include the polyfill
// https://github.com/inexorabletash/text-encoding
window.TextEncoder || document.write("<script src=js/encoding-polyfill.js><\/script>");
// If Promise is not supported we include the polyfill
// https://github.com/taylorhakes/promise-polyfill
window.Promise || document.write("<script src=js/promise-polyfill.js><\/script>");
// If setImmediate is not implemented we include the polyfill
window.setImmediate || document.write("<script src=js/" + esdir + "setImmediate-polyfill.js><\/script>");
// Include the scrypt implementation
document.write("<script src=js/" + esdir + "mpw-js/pbkdf2.js><\/script>");
document.write("<script src=js/" + esdir + "mpw-js/scrypt.js?1><\/script>");
// Include the MPW class
document.write("<script src=js/" + esdir + "mpw-js/mpw.js?1><\/script>");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
var mpw, error;
function updateMPW() {
mpw = null;
startWork();
mpw = new MPW( $('#userName')[0].value, $('#masterPassword')[0].value, $('#version')[0].value );
mpw.key.then(
function() {
doneWork();
},
function(reason) {
error = reason;
mpw = null;
doneWork();
}
);
}
function startWork() {
update(true);
}
function doneWork() {
update(false);
}
function update(working) {
var screen = mpw? 'site': 'identity';
// Screen Name
if (screen == 'identity') {
$('#identity').addClass('active');
if (!working)
$('#userName').focus();
}
else {
$('#identity').removeClass('active');
$('#userName')[0].value = $('#masterPassword')[0].value = '';
}
if (screen == 'site') {
$('#site').addClass('active');
if (!working)
$('#siteName').focus();
}
else {
$('#site').removeClass('active');
$('#siteName')[0].value = $('#sitePassword')[0].value = '';
}
// Working
if (working && screen == 'identity')
$('#identity').addClass('working').find('input, select').attr('disabled', 'disabled');
else
$('#identity').removeClass('working').find('input, select').removeAttr('disabled');
if (working && screen == 'site')
$('#site').addClass('working');
else
$('#site').removeClass('working');
// Error
$('#error').text(error);
}
function updateSite() {
if (!mpw) {
doneWork();
return
}
startWork();
mpw.generatePassword( $('#siteName')[0].value, $('#siteCounter')[0].valueAsNumber, $('#siteType')[0].value )
.then( function (sitePassword) {
$('#sitePassword').text(sitePassword);
doneWork();
}, function (reason) {
error = reason;
doneWork();
});
}
function selectText(element) {
var doc = document, range, selection;
if (doc.body.createTextRange) { //ms
range = doc.body.createTextRange();
range.moveToElementText(element);
range.select();
} else if (window.getSelection) { //all others
selection = window.getSelection();
range = doc.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
$(function() {
$('#identity form').on('submit', function() {
updateMPW();
return false;
});
$('#site input, #site select').on('change input keyup', function() {
updateSite();
});
$('#logout').on('click', function() {
mpw = null;
doneWork();
});
$('#sitePassword').on('click', function() {
selectText(this);
});
doneWork();
});

View File

@@ -0,0 +1,186 @@
(function() {
var root;
if (typeof window === 'object' && window) {
root = window;
} else {
root = global;
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = root.Promise ? root.Promise : Promise;
} else if (!root.Promise) {
root.Promise = Promise;
}
// Use polyfill for setImmediate for performance gains
var asap = root.setImmediate || function(fn) { setTimeout(fn, 1); };
// Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function() {
fn.apply(thisArg, arguments);
}
}
var isArray = Array.isArray || function(value) { return Object.prototype.toString.call(value) === "[object Array]" };
function Promise(fn) {
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function') throw new TypeError('not a function');
this._state = null;
this._value = null;
this._deferreds = []
doResolve(fn, bind(resolve, this), bind(reject, this))
}
function handle(deferred) {
var me = this;
if (this._state === null) {
this._deferreds.push(deferred);
return
}
asap(function() {
var cb = me._state ? deferred.onFulfilled : deferred.onRejected
if (cb === null) {
(me._state ? deferred.resolve : deferred.reject)(me._value);
return;
}
var ret;
try {
ret = cb(me._value);
}
catch (e) {
deferred.reject(e);
return;
}
deferred.resolve(ret);
})
}
function resolve(newValue) {
try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.');
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this));
return;
}
}
this._state = true;
this._value = newValue;
finale.call(this);
} catch (e) { reject.call(this, e); }
}
function reject(newValue) {
this._state = false;
this._value = newValue;
finale.call(this);
}
function finale() {
for (var i = 0, len = this._deferreds.length; i < len; i++) {
handle.call(this, this._deferreds[i]);
}
this._deferreds = null;
}
function Handler(onFulfilled, onRejected, resolve, reject){
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.resolve = resolve;
this.reject = reject;
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, onFulfilled, onRejected) {
var done = false;
try {
fn(function (value) {
if (done) return;
done = true;
onFulfilled(value);
}, function (reason) {
if (done) return;
done = true;
onRejected(reason);
})
} catch (ex) {
if (done) return;
done = true;
onRejected(ex);
}
}
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function(onFulfilled, onRejected) {
var me = this;
return new Promise(function(resolve, reject) {
handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject));
})
};
Promise.all = function () {
var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments);
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then;
if (typeof then === 'function') {
then.call(val, function (val) { res(i, val) }, reject);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
Promise.resolve = function (value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value;
}
return new Promise(function (resolve) {
resolve(value);
});
};
Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
for(var i = 0, len = values.length; i < len; i++) {
values[i].then(resolve, reject);
}
});
};
})();

View File

@@ -0,0 +1,211 @@
/*! by Tom Thorogood <me@tomthorogood.co.uk> */
/*! This work is licensed under the Creative Commons Attribution 4.0
International License. To view a copy of this license, visit
http://creativecommons.org/licenses/by/4.0/ or see LICENSE. */
// setImmediate is a 0-delay setTimeout of sorts introduced
// by MS and wrongly held back by other browsers
window.setImmediate || !function (global) {
// Get the global prototype to attach setImmediate to
let attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
// If we couldn't get the prototype or setTimeout wasn't attached
// to the prototype we just attach to global
attachTo && attachTo.setTimeout || (attachTo = global);
// If the MS prefixed implementation exists, use it
if (global.msSetImmediate) {
return attachTo.setImmediate = global.msSetImmediate, attachTo.clearImmediate = global.msClearImmediate;
}
// https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js
// This checks if the current environment is Node.js
if (global.process && Object.prototype.toString(global.process) === "[object process]") {
// If it is we might be able to use timers
let timers = global.require("timers");
// If it implements setImmediate we use it
if (timers && timers.setImmediate) {
return attachTo.setImmediate = timers.setImmediate, attachTo.clearImmediate = timers.clearImmediate;
}
// If it isn't we polyfill with nextTick which is
// sufficiently similar
if (global.process.nextTick) {
return attachTo.setImmediate = function (func, ...params) {
// Invoke func with the params as passed into setImmediate
global.process.nextTick(() => func(...params));
}, attachTo.clearImmediate = function (immediateID) {
// There is no id or way to stop nextTick
throw new Error("clearImmediate not implemented");
};
}
}
// http://dbaron.org/log/20100309-faster-timeouts
// https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js
// https://github.com/kriskowal/q/blob/0428c15d2ffc8e874b4be3a50e92884ef8701a6f/q.js#L125-141
// If we have messaging channels, or we have postMessage and this
// isn't a WebWorker, we can use messaging
if (global.MessageChannel || global.postMessage && !global.importScripts && (function () {
// This checks if global.postMessage is asynchronous,
// it has been known to be buggy and synchronous in
// some browsers
let postMessageIsAsynchronous = true;
let oldOnMessage = global.onmessage;
global.onmessage = function () { postMessageIsAsynchronous = false; };
global.postMessage("", "*");
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
})()) {
// A unique id prefix to ensure that ONLY valid messages are accepted
let messageName = `setImmediate-polyfill-${Math.random()}`.replace("0.", "");
// The numeric identifier of the next dispatched scrypt call
let immediateID = 1;
// The timeout function and arguments, indexed by numeric identifier
let timeouts = { };
// If a MessageChannel exists we can use it to avoid sending
// messages to the browser which could cause interoperability
// issues
let channel = global.MessageChannel && new global.MessageChannel();
// We need to start port1 in order to receive messages sent
// from port2
channel && channel.port1.start();
// Add a handler to the message event of either the message
// channel, if it exists, or global if it does not
(channel && channel.port1 || global).addEventListener("message", function (event) {
// If event data is not a string, i.e. doesn't implement split,
// we didn't send it
if (!event.data || !event.data.split) {
return;
}
// Split the identifier into the name and numeric id
let [name, immediateID] = event.data.split("$");
// If we are not using a MessageChannel check that the source
// of the event was this window, also check the name is valid,
// if either of these are not true, we didn't send it
if (!channel && event.source !== global || name !== messageName) {
return;
}
// Prevent the event from propagating further
event.stopPropagation();
// Retrieve the function and the arguments we will invoke
// leaving func and params as null if the immediateID
// does not exist in timeouts (because clearImmediate has
// been called before we got here)
let [func, params] = timeouts[immediateID] || [ ];
// Invoke the func with the appropriate parameters
func && func(...params);
// Clear func and params for GC
func = params = null;
// Remove key:immediateID from timeouts to ensure it's only
// called once and to allow for GC
delete timeouts[immediateID];
}, false);
return attachTo.setImmediate = function (func, ...params) {
// Store the function and it's arguments in timeouts
timeouts[immediateID] = [func, params];
// Post the message either using port2 of the MessageChannel
// or on global if it's not available w/ the unique id
// If the message is sent on global we dispatch it w/ a
// targetOrigin of "*" (indicating no preference)
(channel && channel.port2 || global).postMessage([messageName, immediateID].join("$"), ...(channel ? [ ] : ["*"]));
// We return a unique numeric id to identify the call
// to setImmediate, this allows it to be cancelled
return immediateID++;
}, attachTo.clearImmediate = function (immediateID) {
// Delete the function and arguments associated
// w/ identifier of immediateID
delete timeouts[immediateID];
};
}
// Set setImmediate to prefixed or non-prefixed requestAnimationFrame
// requestAnimationFrame dispatches at a later point in the event cycle
attachTo.setImmediate = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || global.msRequestAnimationFrame;
// If requestAnimationFrame existed we end, setting clearImmediate
// to cancelAnimationFrame
if (attachTo.setImmediate) {
return attachTo.clearImmediate = global.cancelAnimationFrame || global.mozCancelAnimationFrame || global.webkitCancelAnimationFrame || global.msCancelAnimationFrame || global.webkitCancelRequestAnimationFrame;
}
// https://github.com/YuzuJS/setImmediate/blob/master/setImmediate.js
// We can use a script tag and the readystatechange event on IE(?)
if (global.document && "onreadystatechange" in global.document.getElementsByTagName("script")[0]) {
// The numeric identifier of the next dispatched scrypt call
let immediateID = 1;
// A boolean value to allow clearImmediate to work,
// indexed by numeric identifier
let timeouts = { };
return attachTo.setImmediate = function (func, ...params) {
// Set true in timeouts for immediateID to indicate the func
// should be invoked
timeouts[immediateID] = true;
// Create a script tag that will be added to the DOM
let script = global.document.createElement("script");
// Add a handler for onreadystatechange
script.onreadystatechange = function () {
// If the timeout has not been cancelled, call the func
// w/ the arguments specified
timeouts[immediateID] && func(...params);
// Remove key:immediateID from timeouts to ensure it's only called once
delete timeouts[immediateID];
// Remove the handler to allow GC
script.onreadystatechange = null;
// Remove the script tag from the DOM to ensure GC
global.document.body.removeChild(script);
// Nullify the script variable to allow GC
script = null;
};
// Add the script tag to the DOM to which begins loading
// the tag which will invoke the readystatechange event
global.document.body.appendChild(script);
// Return a unique numeric id to identify the call
// to setImmediate, this allows it to be cancelled
return immediateID++;
}, attachTo.clearImmediate = function (immediateID) {
// Remove key:immediateID from timeouts to prevent func from being called
delete timeouts[immediateID];
};
}
// The worst fallback is setTimeout, although the delay is set to 0,
// in reality this should have a ~20ms delay as this is an important
// part of the spec
attachTo.setImmediate = (func, ...params) => global.setTimeout(func, 0, ...params);
attachTo.clearImmediate = global.clearTimeout;
// Here we check if the arguments passed to setTimeout actually will be
// passed to the callback, on older versions of IE(?) this check will fail
global.setTimeout(function (arg) {
// If the test fails, we wrap func in a closure that will invoke func w/
// the arguments
arg || (attachTo.setImmediate = (func, ...params) => global.setTimeout(() => func(...params), 0));
}, 0, true);
}(this || window);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
platform-independent/web/sync Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE[0]%/*}"
#s3cmd sync --exclude '.git/**' --delete-removed --follow-symlinks --preserve --acl-public --reduced-redundancy . s3://js.masterpassword.app/
rsync -avP --delete --no-group . satura.lyndir.com:/usr/local/www/js.masterpassword.app/htdocs-secure/