+ Add some new concepts to DEVELOPMENT.md
+ Add icon
This commit is contained in:
@@ -1,3 +1,57 @@
|
||||
# Developer notes
|
||||
**Programming language:** TypeScript
|
||||
|
||||
**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.
|
||||
@@ -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
BIN
src/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -4,7 +4,6 @@
|
||||
padding-top: calc(10% + 50px);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
#master_prompt input {
|
||||
margin-top: 50px;
|
||||
@@ -23,3 +22,7 @@
|
||||
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
3
src/html/create.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div id="create_setup">
|
||||
<h1>New QR-Code</h1>
|
||||
</div>
|
||||
@@ -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');
|
||||
@@ -117,7 +128,7 @@
|
||||
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
10
src/js/loader.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* This script loads or unloads different modules present in src/js
|
||||
*/
|
||||
class Loader {
|
||||
index = null;
|
||||
|
||||
constructor(index) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user