diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index bbe1079..9fec453 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -16,13 +16,13 @@ Attackers are using password dictionaries with more then one million passwords.
## QR-Code schema
An OffPass QR-Code must follow this data schema or else OffPass wouldn't be able to read it. **The following examples are data after decryption.**
```
-name|password|email|website_url|(custom1)data1|(custom2)data2
+name|username|password|email|website_url|(custom1)data1|(custom2)data2
```
Als Beispiel:
```
-Main Steam Account|super_secret_example123|info@example.de|https://store.steampowered.com/login/|(2fa_backup)R1337
+Main Steam Account|mondei1|super_secret_example123|info@example.de|https://store.steampowered.com/login/|(2fa_backup)R1337
-ProtonMail|mail_pw123|klier.nicolas@protonmail.com||
+ProtonMail||mail_pw123|klier.nicolas@protonmail.com||
```
These characters are reserved and cannot be used for any fields: `|%§`
@@ -35,9 +35,9 @@ The compression key is stored like that: `§key`, the decryption key is stored l
For example:
```
-%session_key%§xa|passwords_not2134|email_too@example.com|§q|(§a)§gh|(uncompressed)value
+%session_key%§xa|mondei1|passwords_not2134|email_too@example.com|§q|(§a)§gh|(uncompressed)value
--> Google|passwords_not2134|email_either@example.com|https://accounts.google.com|(2fa_backup)245131,...|(uncompressed)value
+-> Google|mondei1|passwords_not2134|email_either@example.com|https://accounts.google.com|(2fa_backup)245131,...|(uncompressed)value
```
This can has two advantages:
@@ -51,7 +51,7 @@ But one disadvantage:
OffPass will first look if the scanned QR-Code is actually an OffPass QR-Code. This is done by checking the first three charcters:
```
-op:jA0ECQMC+t514sews8e70jsBw4SWsYYgPGzi5Ps0OGr8/tVGngopmHDQpSpMkNtkWZU573zNsFyk VVN3elnAY0D+EIIzTpKxq0F3fQ==
+op1:jA0ECQMC+t514sews8e70jsBw4SWsYYgPGzi5Ps0OGr8/tVGngopmHDQpSpMkNtkWZU573zNsFykVVN3elnAY0D+EIIzTpKxq0F3fQ==
```
-This `op:` tells the program that this is actully a OffPass QR-Code. If this is not present, OffPass will abort further steps and notify the user that this is not an OffPass QR-Code.
\ No newline at end of file
+This `op1:` tells the program that this is actully a OffPass QR-Code and which version. If this is not present, OffPass will abort further steps and notify the user that this is not an OffPass QR-Code.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bca77e9..19b1fae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,6 +42,15 @@
"@types/node": "*"
}
},
+ "@types/jquery": {
+ "version": "3.3.31",
+ "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz",
+ "integrity": "sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==",
+ "dev": true,
+ "requires": {
+ "@types/sizzle": "*"
+ }
+ },
"@types/node": {
"version": "12.11.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.6.tgz",
@@ -57,6 +66,12 @@
"@types/bn.js": "*"
}
},
+ "@types/sizzle": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
+ "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
+ "dev": true
+ },
"asn1.js": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz",
@@ -67,6 +82,32 @@
"minimalistic-assert": "^1.0.0"
}
},
+ "babel-polyfill": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
+ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "core-js": "^2.5.0",
+ "regenerator-runtime": "^0.10.5"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+ }
+ }
+ },
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
@@ -131,6 +172,11 @@
"typedarray": "^0.0.6"
}
},
+ "core-js": {
+ "version": "2.6.10",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz",
+ "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA=="
+ },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -193,6 +239,11 @@
"integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==",
"dev": true
},
+ "es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+ },
"extract-zip": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
@@ -242,6 +293,16 @@
"universalify": "^0.1.0"
}
},
+ "fsm-as-promised": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/fsm-as-promised/-/fsm-as-promised-0.13.2.tgz",
+ "integrity": "sha1-X04RCGgotwoZItx7T4HAgX1ugjg=",
+ "requires": {
+ "es6-promise": "^4.0.2",
+ "lodash": "^4.16.2",
+ "stampit": "^3.0.1"
+ }
+ },
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@@ -291,6 +352,17 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "instascan": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/instascan/-/instascan-1.0.0.tgz",
+ "integrity": "sha1-3llJ9z9pj43/aN8Ke89pQqeZ7kQ=",
+ "requires": {
+ "babel-polyfill": "^6.9.1",
+ "fsm-as-promised": "^0.13.0",
+ "visibilityjs": "^1.2.3",
+ "webrtc-adapter": "^1.4.0"
+ }
+ },
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -321,6 +393,11 @@
"json-buffer": "3.0.0"
}
},
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ },
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -446,6 +523,11 @@
"util-deprecate": "~1.0.1"
}
},
+ "regenerator-runtime": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
+ "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg="
+ },
"responselike": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
@@ -470,11 +552,21 @@
"truncate-utf8-bytes": "^1.0.0"
}
},
+ "sdp": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/sdp/-/sdp-1.5.4.tgz",
+ "integrity": "sha1-jgOPbdsUvXZa4fS1IW4SCUUR4NA="
+ },
"slide": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
},
+ "stampit": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/stampit/-/stampit-3.2.1.tgz",
+ "integrity": "sha1-lTpBpJRYoLKG/7HjydbOcDblids="
+ },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -541,6 +633,19 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
+ "visibilityjs": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/visibilityjs/-/visibilityjs-1.2.8.tgz",
+ "integrity": "sha512-Y+aL3OUX88b+/VSmkmC2ApuLbf0grzbNLpCfIDSw3BzTU6PqcPsdgIOaw8b+eZoy+DdQqnVN3y/Evow9vQq9Ig=="
+ },
+ "webrtc-adapter": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-1.4.0.tgz",
+ "integrity": "sha1-WCiaY9BUxls2+w7zieovbQx/XZg=",
+ "requires": {
+ "sdp": "^1.0.0"
+ }
+ },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/package.json b/package.json
index 759b0ea..e99131f 100644
--- a/package.json
+++ b/package.json
@@ -4,16 +4,17 @@
"description": "OffPass, a special password manager. All your passwords are not stored in an encrypted container but offline.",
"main": "index.js",
"dependencies": {
+ "instascan": "^1.0.0",
"openpgp": "^4.6.2"
},
"devDependencies": {
+ "@types/jquery": "^3.3.31",
"@types/openpgp": "^4.4.7",
"electron": "^7.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "start": "tsc && electron src/main.js",
- "clean": "del /S src\\js\\*.js && del src\\*.js"
+ "start": "tsc && electron src/main.js"
},
"repository": {
"type": "git",
diff --git a/src/css/choose.css b/src/css/choose.css
index 9105ddb..ba4b51e 100644
--- a/src/css/choose.css
+++ b/src/css/choose.css
@@ -21,8 +21,10 @@
}
.options div:hover {
+ transform: scale(1.02);
+ box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.5);
background-color: rgba(255, 255, 255, .2);
- border: 1px rgba(255, 255, 255, 0.8) solid;
+ border: 1px rgba(255, 255, 255, 0.9) solid;
}
.options div i {
diff --git a/src/css/create.css b/src/css/create.css
index 12a0128..3167099 100644
--- a/src/css/create.css
+++ b/src/css/create.css
@@ -7,39 +7,40 @@
}
#create_setup .options {
display: grid;
- grid-template-columns: 1fr 1fr 1fr;
- grid-template-rows: .25fr .25fr .25fr;
- row-gap: 50px;
- column-gap: 50px;
+ grid-template-columns: .2fr 1fr;
+ grid-template-rows: repeat(8, 1fr);
+ row-gap: 20px;
+ column-gap: 90px;
margin-left: 50px;
width: calc(100% - 100px);
}
+#name, #username, #url, #email, #password, #algorithm, #advanced {
+ grid-column: 2;
+}
+
+label {
+ grid-column: 1;
+ line-height: 2;
+ text-align: right !important;
+}
+
#name {
- grid-column: 1;
grid-row: 1;
}
-
-#url {
- grid-column: 2 / 4;
- grid-row: 1;
-}
-
-#email {
- grid-column: 1;
+#username {
grid-row: 2;
}
-
-#password {
- grid-column: 1 / 4;
+#email {
grid-row: 3;
- display: inline-block;
- border: 1px solid white;
}
-#password input {
- border: none;
+#password {
+ grid-row: 4;
+ width: 100%;
}
-#password i {
-
- border-left: none;
+#url {
+ grid-row: 5;
+}
+#algorithm {
+ grid-row: 6;
}
\ No newline at end of file
diff --git a/src/css/global.css b/src/css/global.css
index a325026..19b5be1 100644
--- a/src/css/global.css
+++ b/src/css/global.css
@@ -9,12 +9,13 @@ body {
/*background-color: #2A2C2B;*/
overflow: hidden;
}
+
h1, h3 {
font-family: 'Oxygen', sans-serif;
color: #fff;
}
-p, span {
+p, span, label {
font-family: 'Source Sans Pro', sans-serif;
color: #fff;
}
@@ -55,6 +56,28 @@ input:focus {
outline: none;
}
+.inputico {
+ position: relative;
+ display: inline-block;
+ border: 1px solid white !important;
+}
+.inputico:focus {
+ border: 1px rgb(200, 200, 200, 1) solid;
+ background-color: rgba(29, 29, 29, 0.3);
+ color: rgb(200, 200, 200, 1);
+ outline: none;
+}
+.inputico input {
+ border: none !important;
+ width: 97%;
+}
+.inputico i {
+ height: 15px;
+ position: absolute;
+ right:0;
+ top:5px;
+}
+
#taskbar {
display: flex;
position: absolute;
diff --git a/src/css/scanner.css b/src/css/scanner.css
index 6dcdbe1..92576f6 100644
--- a/src/css/scanner.css
+++ b/src/css/scanner.css
@@ -28,14 +28,14 @@
z-index: -5;
opacity: 0;
padding-left: 20px;
- backdrop-filter: blur(20px);
- background: linear-gradient(0deg, rgba(0,0,0,.75) 80%, rgba(94,94,94,.75) 100%);
+ backdrop-filter: blur(50px);
+ background-color: rgba(0,0,0,0.4);
box-shadow: 0px -10px 34px -10px rgba(0,0,0,0.75);
}
#password {
font-family: 'Consolas';
- font-weight: 200;
+ font-weight: 100;
}
.show {
diff --git a/src/css/welcome.css b/src/css/welcome.css
new file mode 100644
index 0000000..f986d63
--- /dev/null
+++ b/src/css/welcome.css
@@ -0,0 +1,29 @@
+#welcome {
+ position: absolute;
+ display: flex;
+ width: 100%;
+ height: 100%;
+ vertical-align: middle;
+ align-content: center;
+}
+
+#welcome div {
+ text-align: center;
+ width: 100vw;
+ margin-left: auto;
+ margin-right: auto;
+ margin-top: 15%;
+ display: block;
+}
+
+#welcome img {
+ height: 166px;
+ width: 150px;
+}
+
+#welcome a {
+ font-size: 16pt;
+}
+#welcome i {
+ padding-left: 20px;
+}
\ No newline at end of file
diff --git a/src/html/create.html b/src/html/create.html
index 26e2811..e0b879a 100644
--- a/src/html/create.html
+++ b/src/html/create.html
@@ -1,12 +1,24 @@
\ No newline at end of file
diff --git a/src/html/index.html b/src/html/index.html
index a7ef490..3f8f72e 100644
--- a/src/html/index.html
+++ b/src/html/index.html
@@ -5,6 +5,8 @@
OffPass
+
+
@@ -24,8 +26,6 @@
Welcome to OffPass
Please enter your master password
-
- Or create a new QR-Code
@@ -38,11 +38,26 @@
window.$ = window.jQuery = require('../assets/jquery-3.4.1.min.js');
const { Loader } = require('../js/loader.js');
+ const { Format } = require('../js/format.js');
const { remote } = require('electron');
const pgp = require('openpgp');
let master_password = "";
const loader = new Loader(document.getElementById('content'));
+ const format = new Format("%AzTka312K1%Google|mondei1|passwords_not2134|email_either@example.com|https://accounts.google.com|(2fa_backup)245131,...|(uncompressed)value");
+ format.parse();
+
+ console.log(format.build({
+ decryptionKey: "az35LKuJ$w",
+ title: "Steam",
+ password: "lol_my-password1412%$",
+ custom: [
+ {
+ key: "2fa_secret",
+ value: "R24829"
+ }
+ ]
+ }));
// Slide
$('#master').keypress(async (e) => {
@@ -85,55 +100,7 @@
error.css('bottom', '-500px;')
}, 5000);
- }
-
- async function startWatching() {
- return new Promise((resolve, reject) => {
- Instascan.Camera.getCameras().then(function (cameras) {
- if (cameras.length > 0) {
- scanner.start(cameras[0]);
- navigator.mediaDevices.getUserMedia({video: true}).then((stream) => {
- preview.srcObject = stream;
- resolve(true);
- })
- } else {
- resolve(false);
- }
- }).catch(function (e) {
- showError("No camera found.");
- $('#wait').animate({
- left: '150%'
- }, 200);
- $('#master_prompt').animate({
- left: '0'
- }, 100);
- });
-
- navigator.mediaDevices.getUserMedia({video: true});
- const preview = document.getElementById('preview');
-
- let scanner = new Instascan.Scanner({ video: preview, mirror: false });
-
- scanner.addListener('scan', async (content) => {
- content = "-----BEGIN PGP MESSAGE-----\n\n" + content + "\n-----END PGP MESSAGE-----";
- console.log(content)
- pgp.decrypt({
- message: await pgp.message.readArmored(content),
- passwords: [ master_password ],
- format: 'string'
- }).then((plaintext) => {
- const scanned = $('#scanned');
- scanned.css('top', '85%');
- scanned.css('z-index', '2');
- scanned.addClass("fadeInUp");
- $('#password').text(plaintext.data)
- console.log(plaintext.data);
- }).catch((err) => {
- if (err) showError("Wrong master password");
- })
- });
- })
- }
+ }
function slide_left(from, to, duration = 300) {
$('#' + from).animate({
diff --git a/src/html/welcome.html b/src/html/welcome.html
new file mode 100644
index 0000000..2c7b668
--- /dev/null
+++ b/src/html/welcome.html
@@ -0,0 +1,8 @@
+
+
+

+
Welcome
+
to OffPass, the next level of password storage.
+
Let's start
+
+
\ No newline at end of file
diff --git a/src/js/create.ts b/src/js/create.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/js/format.js b/src/js/format.js
new file mode 100644
index 0000000..b2fea49
--- /dev/null
+++ b/src/js/format.js
@@ -0,0 +1,81 @@
+"use strict";
+/**
+ * This script is responsible to handle the data stored on QR-Codes.
+ * For detailed information, please take a look at DEVELOPMENT.md in
+ * the projects root directory.
+ */
+exports.__esModule = true;
+var Format = /** @class */ (function () {
+ /**
+ * Format handler
+ * @param text The unparsed text provided on a QR-Code
+ */
+ function Format(text) {
+ // Example: %session_key%§xa|mondei1|passwords_not2134|email_too@example.com|§q|(§a)§gh|(uncompressed)value
+ // Google|mondei1|passwords_not2134|email_either@example.com|https://accounts.google.com|(2fa_backup)245131,...|(uncompressed)value
+ this.text = null;
+ this.format = {
+ title: "",
+ username: "",
+ password: "",
+ email: "",
+ loginURL: null
+ };
+ this.text = text;
+ }
+ Format.prototype.isCompatible = function () {
+ return true;
+ };
+ Format.prototype.decrypt = function () {
+ if (this.isCompatible()) {
+ }
+ };
+ Format.prototype.parse = function () {
+ var parts = this.text.split('|');
+ // Parse static values
+ this.format.title = parts[0];
+ // Check if a decryption key exists at the start
+ if (this.format.title.startsWith('%')) {
+ this.format.decryptionKey = "";
+ // Extract
+ for (var i = 1; i < this.format.title.length; i++) {
+ var char = this.format.title.charAt(i);
+ if (char != '%') {
+ this.format.decryptionKey += char;
+ }
+ else {
+ break;
+ }
+ }
+ // Remove decryption key from first value
+ this.format.title = this.format.title.substr(this.format.decryptionKey.length + 2, this.format.title.length); // + 2 because of the two '%'
+ }
+ this.format.username = parts[1];
+ this.format.password = parts[2];
+ this.format.email = parts[3];
+ this.format.loginURL = new URL(parts[4]);
+ console.log(this.format);
+ };
+ Format.prototype.build = function (data) {
+ if (data == undefined)
+ data = this.format;
+ var raw = "";
+ if (data.decryptionKey != undefined) {
+ raw += "%" + data.decryptionKey + "%";
+ }
+ raw += data.title + "|";
+ raw += data.username + "|";
+ raw += data.password + "|";
+ raw += data.email + "|";
+ raw += data.loginURL + "|";
+ if (data.custom != undefined) {
+ data.custom.forEach(function (element) {
+ raw += "(" + element.key + ")" + element.value + "|";
+ });
+ }
+ raw = raw.substr(0, raw.length - 1); // Remove the last |
+ return raw;
+ };
+ return Format;
+}());
+exports.Format = Format;
diff --git a/src/js/format.ts b/src/js/format.ts
new file mode 100644
index 0000000..ceeab79
--- /dev/null
+++ b/src/js/format.ts
@@ -0,0 +1,105 @@
+/**
+ * This script is responsible to handle the data stored on QR-Codes.
+ * For detailed information, please take a look at DEVELOPMENT.md in
+ * the projects root directory.
+ */
+
+export interface ICustom {
+ key: string;
+ value: string;
+}
+
+export interface IFormat {
+ decryptionKey?: string;
+ title: string;
+ username?: string;
+ password: string;
+ email?: string;
+ loginURL?: URL;
+ custom?: Array
;
+}
+
+export class Format {
+ // Example: %session_key%§xa|mondei1|passwords_not2134|email_too@example.com|§q|(§a)§gh|(uncompressed)value
+ // Google|mondei1|passwords_not2134|email_either@example.com|https://accounts.google.com|(2fa_backup)245131,...|(uncompressed)value
+ text: string = null;
+ format: IFormat = {
+ title: "",
+ username: "",
+ password: "",
+ email: "",
+ loginURL: null
+ };
+
+ /**
+ * Format handler
+ * @param text The unparsed text provided on a QR-Code
+ */
+ constructor(text?: string) {
+ this.text = text;
+ }
+
+ isCompatible(): boolean {
+ return true;
+ }
+
+ decrypt() {
+ if(this.isCompatible()) {
+
+ }
+ }
+
+ parse() {
+ const parts = this.text.split('|');
+
+ // Parse static values
+ this.format.title = parts[0];
+
+ // Check if a decryption key exists at the start
+ if(this.format.title.startsWith('%')) {
+ this.format.decryptionKey = "";
+ // Extract
+ for(let i = 1; i < this.format.title.length; i++) {
+ const char = this.format.title.charAt(i);
+
+ if (char != '%') {
+ this.format.decryptionKey += char;
+ } else {
+ break;
+ }
+ }
+ // Remove decryption key from first value
+ this.format.title = this.format.title.substr(this.format.decryptionKey.length + 2, this.format.title.length) // + 2 because of the two '%'
+ }
+
+ this.format.username = parts[1];
+ this.format.password = parts[2];
+ this.format.email = parts[3];
+ this.format.loginURL = new URL(parts[4]);
+
+ console.log(this.format);
+ }
+
+ build(data?: IFormat): string {
+ if (data == undefined) data = this.format;
+
+ let raw = "";
+ if(data.decryptionKey != undefined) {
+ raw += "%" + data.decryptionKey + "%";
+ }
+ raw += data.title + "|";
+ raw += data.username + "|";
+ raw += data.password + "|";
+ raw += data.email + "|";
+ raw += data.loginURL + "|";
+
+ if (data.custom != undefined) {
+ data.custom.forEach(element => {
+ raw += `(${element.key})${element.value}|`;
+ });
+ }
+ raw = raw.substr(0, raw.length - 1); // Remove the last |
+
+ return raw;
+ }
+}
\ No newline at end of file
diff --git a/src/js/loader.js b/src/js/loader.js
index 3553e83..626ffbc 100644
--- a/src/js/loader.js
+++ b/src/js/loader.js
@@ -61,11 +61,6 @@ var Loader = /** @class */ (function () {
css.setAttribute("href", "../css/" + module_name + ".css");
_this.head.appendChild(css);
var html_path = path_1.resolve("src/html/", module_name + ".html");
- // Load js file
- var js = document.createElement("script");
- js.setAttribute("type", "text/javascript");
- js.setAttribute("src", "../js/" + module_name + ".js");
- _this.head.appendChild(js);
// Load html file
fs_1.readFile(html_path, 'utf8', function (err, data) {
if (err) {
@@ -74,6 +69,11 @@ var Loader = /** @class */ (function () {
}
else {
_this.content.innerHTML += data;
+ // Load js file
+ var js = document.createElement("script");
+ js.setAttribute("type", "text/javascript");
+ js.setAttribute("src", "../js/" + module_name + ".js");
+ _this.head.appendChild(js);
// Push to loaded modules
var mod = { name: module_name, js: js, css: css, html: document.getElementById(module_name) };
_this.modules.push(mod);
@@ -85,22 +85,15 @@ var Loader = /** @class */ (function () {
});
};
Loader.prototype.unload = function (module_name) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- this.modules.forEach(function (element) {
- if (element.name == module_name) {
- console.log(element);
- /*
- element.js.parentElement.removeChild(element.js);
- element.css.parentElement.removeChild(element.css);
- element.html.parentElement.removeChild(element.html);*/
- element.html.remove();
- element.css.remove();
- element.js.remove();
- }
- });
- return [2 /*return*/];
- });
+ this.modules.forEach(function (element) {
+ if (element.name == module_name) {
+ console.log(element);
+ element.html.remove();
+ element.css.remove();
+ element.js.remove();
+ return;
+ }
+ console.error("Tried to remove module which is not loaded:", module_name);
});
};
return Loader;
diff --git a/src/js/loader.ts b/src/js/loader.ts
index 18c051c..5669b97 100644
--- a/src/js/loader.ts
+++ b/src/js/loader.ts
@@ -32,12 +32,6 @@ export class Loader {
const html_path: string = res("src/html/", module_name + ".html");
- // Load js file
- const js = document.createElement("script");
- js.setAttribute("type", "text/javascript");
- js.setAttribute("src", "../js/" + module_name + ".js");
- this.head.appendChild(js);
-
// Load html file
readFile(html_path, 'utf8', (err, data) => {
if (err) {
@@ -46,6 +40,12 @@ export class Loader {
}
else {
this.content.innerHTML += data;
+
+ // Load js file
+ const js = document.createElement("script");
+ js.setAttribute("type", "text/javascript");
+ js.setAttribute("src", "../js/" + module_name + ".js");
+ this.head.appendChild(js);
// Push to loaded modules
const mod: IModules = { name: module_name, js: js, css: css, html: document.getElementById(module_name) };
@@ -57,18 +57,17 @@ export class Loader {
});
}
- async unload(module_name) {
+ unload(module_name) {
this.modules.forEach(element => {
if (element.name == module_name) {
console.log(element);
- /*
- element.js.parentElement.removeChild(element.js);
- element.css.parentElement.removeChild(element.css);
- element.html.parentElement.removeChild(element.html);*/
+
element.html.remove();
element.css.remove();
element.js.remove();
+ return;
}
+ console.error("Tried to remove module which is not loaded:", module_name);
});
}
}
\ No newline at end of file
diff --git a/src/js/scanner.js b/src/js/scanner.js
index e69de29..213b347 100644
--- a/src/js/scanner.js
+++ b/src/js/scanner.js
@@ -0,0 +1,47 @@
+async function startWatching() {
+ return new Promise((resolve, reject) => {
+ Instascan.Camera.getCameras().then(function (cameras) {
+ if (cameras.length > 0) {
+ scanner.start(cameras[0]);
+ navigator.mediaDevices.getUserMedia({video: true}).then((stream) => {
+ preview.srcObject = stream;
+ resolve(true);
+ })
+ } else {
+ resolve(false);
+ }
+ }).catch(function (e) {
+ showError("No camera found.");
+ $('#wait').animate({
+ left: '150%'
+ }, 200);
+ $('#master_prompt').animate({
+ left: '0'
+ }, 100);
+ });
+
+ navigator.mediaDevices.getUserMedia({video: true});
+ const preview = document.getElementById('preview');
+
+ let scanner = new Instascan.Scanner({ video: preview, mirror: false });
+
+ scanner.addListener('scan', async (content) => {
+ content = "-----BEGIN PGP MESSAGE-----\n\n" + content + "\n-----END PGP MESSAGE-----";
+ console.log(content)
+ pgp.decrypt({
+ message: await pgp.message.readArmored(content),
+ passwords: [ master_password ],
+ format: 'string'
+ }).then((plaintext) => {
+ const scanned = $('#scanned');
+ scanned.css('top', '85%');
+ scanned.css('z-index', '2');
+ scanned.addClass("fadeInUp");
+ $('#password').text(plaintext.data)
+ console.log(plaintext.data);
+ }).catch((err) => {
+ if (err) showError("Wrong master password");
+ })
+ });
+ })
+}
\ No newline at end of file
diff --git a/src/js/welcome.js b/src/js/welcome.js
new file mode 100644
index 0000000..6ac9896
--- /dev/null
+++ b/src/js/welcome.js
@@ -0,0 +1,8 @@
+const welcome_container = document.getElementById('welcome');
+
+$('#welcome a').click((e) => {
+ $('#master_prompt').css('left', '150%');
+ $('#master_prompt').css('display', 'block');
+ slide_left('welcome', 'master_prompt');
+ $('#master_prompt').css('left', '0');
+});
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 7a73a41..cbf48e3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,2 +1,6 @@
{
+ "compilerOptions": {
+ "allowSyntheticDefaultImports": true,
+ "module": "commonjs"
+ }
}
\ No newline at end of file