+ Add some new concepts to DEVELOPMENT.md

+ Add icon
This commit is contained in:
2019-10-26 15:56:25 +02:00
parent 0fa86ee35c
commit c258d995cf
12 changed files with 117 additions and 29 deletions

View File

@@ -1,3 +1,57 @@
# Developer notes
**Programming language:** TypeScript
**Framework:** Electron (latest)
**Framework:** Electron (latest)
## Encryption
OffPass uses PGP in the background with symmetric encrpytion.
### Passphrase
The passphrase is the actual key which is used for encrpytion. But we don't use your entered master password directly. We hash it with `Argon2i` about 25 times. This gives use an output like this: `29dbf5392f13f36d7e9509b1a5c9add0d6a8e2625b5e84ab4d1df8da6063625d`.
This value will be used as passphrase, not your plain password. The creation of this hash takes about **more then one second**.
Attackers are using password dictionaries with more then one million passwords. Creating such a hash for each password in there would take **millions of years**.
## 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
```
Als Beispiel:
```
Main Steam Account|super_secret_example123|info@example.de|https://store.steampowered.com/login/|(2fa_backup)R1337
ProtonMail|mail_pw123|klier.nicolas@protonmail.com||
```
These characters are reserved and cannot be used for any fields: `|%§`
### Compression
It is possible to compress QR-Codes. Instead of writing all data to the QR-Code you can write random strings (key) to it. OffPass itself holds a database of those random string and the corresponding encrypted value.
The program generates a `session key` (length of 10 characters) which is unique for each QR-Code. This session key is stored on the QR-Code and is used to encrypt the raw values in database. So not even If someone stells your database he wouldn't be able to read your compressed strings.
The compression key is stored like that: `§key`, the decryption key is stored like that: `%decryption_key%` always at the beginning.
For example:
```
%session_key%§xa|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
```
This can has two advantages:
* An attacker can't read compressed values (he would need the database)
* You can get more data on one QR-Code
But one disadvantage:
* **If you lose access to the compression database, you also lose access to those compressed values. But not to your password.**
### Type mark
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==
```
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.

View File

@@ -12,7 +12,8 @@
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "tsc && electron dist/main.js"
"start": "tsc && electron src/main.js",
"clean": "del /S src\\js\\*.js && del src\\*.js"
},
"repository": {
"type": "git",

BIN
src/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -19,6 +19,13 @@ p, span {
color: #fff;
}
a {
color: white;
text-decoration: none;
font-weight: bold;
font-family: 'Source Sans Pro', sans-serif;
}
#taskbar {
display: flex;
position: absolute;
@@ -40,7 +47,6 @@ p, span {
height: 100%;
vertical-align: middle;
text-align: cover;
object-fit: fill;
}
#content div {
position: absolute;

View File

@@ -29,7 +29,8 @@
opacity: 0;
padding-left: 20px;
backdrop-filter: blur(20px);
background-color: rgba(0, 0, 0, .3);
background: linear-gradient(0deg, rgba(0,0,0,.75) 80%, rgba(94,94,94,.75) 100%);
box-shadow: 0px -10px 34px -10px rgba(0,0,0,0.75);
}
#password {

View File

@@ -4,7 +4,6 @@
padding-top: calc(10% + 50px);
width: 100%;
height: 100%;
pointer-events: none;
}
#master_prompt input {
margin-top: 50px;
@@ -22,4 +21,8 @@
border-bottom-width: 3px;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
#master_prompt a {
line-height: 5;
font-size: 13pt;
}

3
src/html/create.html Normal file
View File

@@ -0,0 +1,3 @@
<div id="create_setup">
<h1>New QR-Code</h1>
</div>

View File

@@ -2,17 +2,17 @@
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<title>OffPass</title>
<script type="text/javascript" src="assets/instascan.min.js"></script>
<script type="text/javascript" src="../assets/instascan.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Oxygen:700&display=swap" rel="stylesheet">
<link href="assets/fontawesome-5.11.2/css/all.css" rel="stylesheet">
<link href="assets/animate.css" rel="stylesheet">
<link href="global.css" rel="stylesheet">
<link href="start.css" rel="stylesheet">
<link href="scanner.css" rel="stylesheet">
<link href="../assets/fontawesome-5.11.2/css/all.css" rel="stylesheet">
<link href="../assets/animate.css" rel="stylesheet">
<link href="../css/global.css" rel="stylesheet">
<link href="../css/start.css" rel="stylesheet">
<link href="../css/scanner.css" rel="stylesheet">
</head>
<body>
<div id="taskbar">
@@ -24,7 +24,9 @@
<div id="master_prompt">
<h1>Welcome to OffPass</h1>
<p>Please enter your master password for decryption</p>
<input id="master" placeholder="Master password..." type="password" autofocus>
<input id="master" placeholder="Master password..." type="password" autofocus><br>
<a href="#">Or create a new QR-Code</a>
</div>
<div id="wait">
<h1>Wait for camera...</h1>
@@ -39,7 +41,7 @@
</div>
<script>
window.$ = window.jQuery = require('../src/assets/jquery-3.4.1.min.js');
window.$ = window.jQuery = require('../assets/jquery-3.4.1.min.js');
const { remote } = require('electron');
const pgp = require('openpgp');
@@ -64,6 +66,15 @@
}
});
$('#master_prompt a').click((e) => {
$('#master_prompt').animate({
left: '-150%'
});
$('#create_setup').animate({
left: '0'
});
});
function showError(message) {
const error = $('#error');
error.css('bottom', '50px');
@@ -112,12 +123,12 @@
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'
message: await pgp.message.readArmored(content),
passwords: [ master_password ],
format: 'string'
}).then((plaintext) => {
const scanned = $('#scanned');
scanned.css('top', '80%');
scanned.css('top', '85%');
scanned.css('z-index', '2');
scanned.addClass("fadeInUp");
$('#password').text(plaintext.data)

10
src/js/loader.js Normal file
View File

@@ -0,0 +1,10 @@
/**
* This script loads or unloads different modules present in src/js
*/
class Loader {
index = null;
constructor(index) {
}
}

View File

@@ -1,6 +1,7 @@
"use strict";
exports.__esModule = true;
var electron_1 = require("electron");
var path_1 = require("path");
var win = null;
electron_1.app.on('ready', function () {
win = new electron_1.BrowserWindow({
@@ -12,11 +13,11 @@ electron_1.app.on('ready', function () {
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true
}
},
backgroundColor: '#1c1c1c',
icon: path_1.join(__dirname, '../src/assets/icon.png')
});
//win.loadURL("https://google.de")
win.loadFile('../src/index.html');
win.setTitle("OffPass");
win.loadFile('html/index.html');
});
electron_1.app.on('window-all-closed', function () {
process.exit(0);

View File

@@ -1,4 +1,5 @@
import { app, BrowserWindow } from 'electron';
import { join } from 'path';
let win: BrowserWindow = null;
app.on('ready', () => {
@@ -11,11 +12,11 @@ app.on('ready', () => {
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true
}
},
backgroundColor: '#1c1c1c',
icon: join(__dirname, '../src/assets/icon.png')
});
//win.loadURL("https://google.de")
win.loadFile('../src/index.html');
win.setTitle("OffPass")
win.loadFile('html/index.html');
});
app.on('window-all-closed', () => {

View File

@@ -1,5 +1,2 @@
{
"compilerOptions": {
"outDir": "dist/"
}
}