updated readme and new packed binaries for v1.2

This commit is contained in:
2020-10-02 22:13:25 +02:00
parent 89df06bc77
commit 2b739f3d8b
9 changed files with 317 additions and 48 deletions

View File

@@ -76,6 +76,7 @@ Dazu muss golgender Befehl ausgeführt werden:
ts-node .\index.ts --privkey private.key --decrypt --safe .\out\credentials.json
## Config-Datei
```
{
"mail":{
"host": "<mailserver>",
@@ -91,10 +92,9 @@ ts-node .\index.ts --privkey private.key --decrypt --safe .\out\credentials.json
}
},
"mailFrom": "<absender name>",
"outFileCodes": "<ausgabedatei codes>",
"outFileMatch": "<ausgabedatei regex>"
}
```
## Syntax
@@ -106,6 +106,8 @@ z.B. `ts-node .\index.ts --privkey private.key --pubkey public.key --genkey`
==> Codes Erzeugen und versenden
`ts-node .\index.ts --config <path-to-config-key> --pubkey <path-to-public-key> --send --safe .\out\credentials.json --mails <path-to-mail-list> -html <path-to-html-template>`
Achtung: Es wird im Safe geprüft, ob Mailadressen bereits "bedient" wurden. Sollte dies der Fall sein, werden keine Mails an diese Adresse gesendet. Dies lässt sich mit dem Schalter `--force` umgehen.
z.B. `ts-node .\index.ts --config config.json --pubkey public.key --send --safe .\out\credentials.json --mails mail.txt -html template.html`
==> Safe entschlüsseln
@@ -113,8 +115,13 @@ z.B. `ts-node .\index.ts --config config.json --pubkey public.key --send --safe
z.B. `ts-node .\index.ts --privkey private.key --decrypt --safe .\out\credentials.json`
## Kompillierte Binaries
Die Kompilierten Binaries sind für Linux, MacOS und Windoof verfügbar: [Binaries](https://gitlab.dennisgunia.de/dennisgunia/one-time-code-js/-/tree/master/bin)`
### Erweiterte Schalter
- `--dryrun` : Mails werden nicht versendet und der Safe wird nicht verändert.
- `--force` : Alle Codes werden neu generiert und alle mails werden gesendet. Ignoriere bereits gesendete mails.
## Gepackte Binaries
Die gepackten Binaries sind für Linux, MacOS und Windoof verfügbar: [Binaries](https://gitlab.dennisgunia.de/dennisgunia/one-time-code-js/-/tree/master/bin)
Die befehle ändern sich wie folgt:
@@ -132,3 +139,55 @@ z.B. `./opentoken --config config.json --pubkey public.key --send --safe .\out\c
`./opentoken --privkey <path-to-private-key> --decrypt --safe .\out\credentials.json`
z.B. `./opentoken --privkey private.key --decrypt --safe .\out\credentials.json`
## Ausführen des Quellcodes
Der Sourcecode kann auch über ts-node ausgeführt werden.
Dazu ist Node.js Version 12 zu verwenden
`nvm use 12`
Zum Ausführen sind folgende npm Pakete notwendig:
- typescript
- tslint
- ts-node
Installerien sie diese mit:
`npm install -g typescript tslint ts-node`
Clonen sie dieses Repository auf ihren lokalen rechner und wechseln sie anschließend in dessen verzeichniss:
`git clone https://gitlab.dennisgunia.de/dennisgunia/one-time-code-js.git`
`cd one-time-code-js`
Installieren sie alle lokalen npm Pakete
`npm install`
Kopieren Sie die Config-Template und passen Sie die SMTP-Zugangsdaten an:
`cp config.template.json config.json`
`vim config.json`
Das Skript kann nun über `npm run-script exec` oder `ts-node index.js` ausgeführt werden.
## Packen des Quellcodes
Zum Packen des Quellcodes ist das npm-Paket `pkg` zu installieren:
`npm install -g pkg`
Anschließend wird der Code in JS transpiliert und durch pkg gepackt:
`npm run-script build`
Die Binaries werden in `./bin` gespeichert. Diese sind auch auf Systemen ohne node.js ausführbar.
## Was landet im Safe?
Im safe landen die verschlüsselten Zuordnungen zwischen Codes und Mailadressen.
Zudem werden die verwendeten Codes und die bereits gesendeten Mailadressen seperat voneinander und zufällig gemischt in Klartext gespeichert.
Dies ermöglicht es, nachträglich benutzer hinzuzufügen, ohne allen anderen neue Mails oder gar neue Codes zukommen lassen zu müssen.
Es ist jedoch nocht empfohlen, nachträglich mails hinzuzufügen, da dies, abhängig von der Menge der gleichzeitig hinzugefügten Adressen eine grobe oder ggf. auch sehr genaue zuordnung zwischen Code und Mail der Nachzügler möglich ist.

Binary file not shown.

Binary file not shown.

Binary file not shown.

35
dist/index.js vendored
View File

@@ -22,7 +22,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
var fs = __importStar(require("fs"));
var generate_1 = require("./src/generate");
var vault_1 = require("./src/vault");
var configPath = "", action = -1, pubKey = "", privKey = "", safeFile = "", mails = "", html = "";
var configPath = "", action = -1, pubKey = "", privKey = "", safeFile = "", mails = "", html = "", dryrun = false, force = false;
for (var i = 1; i < process.argv.length; i++) {
if (process.argv[i] === "--config") {
if (i + 1 < process.argv.length && !process.argv[i + 1].startsWith("--")) {
@@ -81,6 +81,12 @@ for (var i = 1; i < process.argv.length; i++) {
if (process.argv[i] === "--genkey") {
action = 3;
}
if (process.argv[i] === "--dryrun") {
dryrun = true;
}
if (process.argv[i] === "--force") {
force = true;
}
}
if (action == -1) {
throw new Error("No Action specified");
@@ -107,18 +113,39 @@ if (action == 1) {
var dataSafe_1 = new vault_1.SecureVault(pubKey, privKey);
var confRaw = fs.readFileSync(configPath, 'utf8');
var config = {};
var addition_1 = false;
config = JSON.parse(confRaw);
if (fs.existsSync(safeFile)) {
dataSafe_1.loadData(safeFile);
config.usedTokens = dataSafe_1.getStorage(dataSafe_1.findStorage("usedTokens")[0].u);
config.usedMails = dataSafe_1.getStorage(dataSafe_1.findStorage("usedMails")[0].u);
addition_1 = true;
}
else {
config.usedTokens = [];
config.usedMails = [];
}
try {
config = JSON.parse(confRaw);
config.inFileMail = mails;
config.htmlPath = html;
config.dryrun = dryrun;
config.force = force;
}
catch (error) {
console.error("Cannote read config file!");
process.exit(100);
}
generate_1.generateToken(config, dataSafe_1).then(function (el) {
console.log(el);
dataSafe_1.saveData(safeFile);
if (addition_1) {
dataSafe_1.setStorage(dataSafe_1.findStorage("usedTokens")[0].u, el.codes);
dataSafe_1.setStorage(dataSafe_1.findStorage("usedMails")[0].u, el.mails);
dataSafe_1.saveData(safeFile);
}
else {
dataSafe_1.pushStorage('usedTokens', el.codes);
dataSafe_1.pushStorage('usedMails', el.mails);
dataSafe_1.saveData(safeFile);
}
}).catch(function (err) { return console.error("error", err); });
}
else if (action == 2) {

118
dist/src/generate.js vendored
View File

@@ -54,55 +54,66 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateToken = void 0;
var fs = __importStar(require("fs"));
var path = __importStar(require("path"));
var nodemailer = __importStar(require("nodemailer"));
var cliProgress = __importStar(require("cli-progress"));
var shuffle_1 = require("./util/shuffle");
var token_1 = require("./util/token");
var Handlebars = __importStar(require("handlebars"));
var mailParser_1 = require("./mailParser");
function generateToken(config, dataSafe) {
var pr = new Promise(function (resolve, error) {
var mailArray = [];
var readline = require('readline'), instream = fs.createReadStream(config.inFileMail), outstream = new (require('stream'))(), rl = readline.createInterface(instream, outstream);
rl.on('line', function (line) {
console.log(line);
mailArray.push({
mail: line.substr(0, line.indexOf(";")),
name: line.substr(line.indexOf(";") + 1)
});
});
rl.on('close', function (line) {
generateCodes(resolve, error, mailArray, config, dataSafe);
return new Promise(function (resolve, error) {
mailParser_1.parseMails(config).then(function (res) {
generateCodes(resolve, error, res, config, dataSafe);
});
});
return pr;
}
exports.generateToken = generateToken;
function generateCodes(resolve, error, mailArray, config, dataSafe) {
return __awaiter(this, void 0, void 0, function () {
var codeArray, checkString, listString, i, code;
var pbar, position, codeArray, checkString, listString, i, code;
return __generator(this, function (_a) {
console.log("\nGenerating codes");
pbar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
pbar.start(mailArray.length, 0, {
speed: "N/A"
});
position = 0;
codeArray = [];
checkString = '';
listString = '';
for (i = 0; i < mailArray.length; i++) {
code = token_1.mkstring(4);
while (codeArray.includes(code)) {
code = '';
do {
code = token_1.mkstring(4);
}
} while ((config.force ? codeArray : __spreadArrays(codeArray, config.usedTokens)).includes(code));
codeArray.push(code);
checkString = checkString + "|" + code;
listString = listString + "\n" + code;
position++;
pbar.update(position);
}
checkString = checkString.substr(1);
listString = listString.substr(1);
pbar.stop();
try {
if (!fs.existsSync(path.dirname(config.outFileMatch))) {
fs.mkdirSync(path.dirname(config.outFileMatch));
}
fs.writeFileSync(config.outFileMatch, checkString);
fs.writeFileSync(config.outFileCodes, listString);
}
catch (error) {
error(error);
catch (err) {
error(err);
}
sendMails(resolve, error, mailArray, codeArray, config, dataSafe);
return [2];
@@ -111,7 +122,7 @@ function generateCodes(resolve, error, mailArray, config, dataSafe) {
}
function sendMails(resolve, error, mailArray, codeArray, config, dataSafe) {
return __awaiter(this, void 0, void 0, function () {
var mailserver, template, htmlSrc, i;
var mailserver, template, htmlSrc, pbar, position, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
@@ -124,26 +135,47 @@ function sendMails(resolve, error, mailArray, codeArray, config, dataSafe) {
console.error("Cannote read template file!");
error(error);
}
console.log("\nSending mails");
pbar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
pbar.start(mailArray.length, 0, {
speed: "N/A"
});
position = 0;
shuffle_1.shuffleArray(mailArray);
shuffle_1.shuffleArray(codeArray);
if (config.force) {
dataSafe.clearVault();
}
i = 0;
_a.label = 1;
case 1:
if (!(i < mailArray.length)) return [3, 4];
dataSafe.pushData({
name: mailArray[i].name,
mail: mailArray[i].mail,
code: codeArray[i]
});
if (!config.dryrun) {
dataSafe.pushData({
name: mailArray[i].name,
mail: mailArray[i].mail,
code: codeArray[i]
});
}
return [4, send(mailArray[i].name, mailArray[i].mail, codeArray[i], template, mailserver, config)];
case 2:
_a.sent();
position++;
pbar.update(position);
_a.label = 3;
case 3:
i++;
return [3, 1];
case 4:
resolve(codeArray);
pbar.stop();
shuffle_1.shuffleArray(mailArray);
shuffle_1.shuffleArray(codeArray);
shuffle_1.shuffleArray(mailArray);
shuffle_1.shuffleArray(codeArray);
resolve({
codes: config.force ? codeArray : (config.dryrun ? config.usedTokens : __spreadArrays(codeArray, config.usedTokens)),
mails: config.force ? mailArray : (config.dryrun ? config.usedMails : __spreadArrays(mailArray, config.usedMails))
});
return [2];
}
});
@@ -155,6 +187,13 @@ function send(name, mail, code, template, mailserver, config) {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!config.dryrun) return [3, 2];
return [4, delay(100)];
case 1:
_a.sent();
console.log("\n\u001B[36m -> dryrun: would send to " + mail + "\u001B[0m");
return [3, 6];
case 2:
html = template({
"name": name,
"mail": mail,
@@ -166,19 +205,26 @@ function send(name, mail, code, template, mailserver, config) {
subject: "Dein Zugangscode zur BJR Wahl",
html: html
};
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4, mailserver.sendMail(mailOptions)];
case 2:
_a.sent();
return [3, 4];
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4, mailserver.sendMail(mailOptions)];
case 4:
_a.sent();
return [3, 6];
case 5:
error_1 = _a.sent();
console.log("Error sendign mail to " + mail + " : " + error_1);
return [3, 4];
case 4: return [2];
return [3, 6];
case 6: return [2];
}
});
});
}
function delay(t, val) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(val);
}, t);
});
}

70
dist/src/mailParser.js vendored Normal file
View File

@@ -0,0 +1,70 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMails = void 0;
var fs = __importStar(require("fs"));
function parseMails(config) {
return new Promise(function (resolve, reject) {
var mailArray = [];
var currSection = "global";
var lineCounter = 0;
var curCounter = 0;
console.log("Reading mails for section " + currSection);
var readline = require('readline'), instream = fs.createReadStream(config.inFileMail), outstream = new (require('stream'))(), rl = readline.createInterface(instream, outstream);
rl.on('line', function (line) {
lineCounter++;
if (line.startsWith('[')) {
if (line.endsWith(']')) {
console.log("Read " + curCounter + " adresses for section " + currSection);
curCounter = 0;
currSection = line.substring(1, line.length - 1);
console.log("Reading mails for section " + currSection);
}
else {
console.error("Error parsing section on line " + lineCounter + ": Syntax Error. Missing closing bracket ]");
}
}
else if (!line.startsWith('#')) {
var ix_1 = line.indexOf(";");
if (ix_1 !== -1) {
if (config.force || config.usedMails.filter(function (el) { return el.mail == line.substr(0, ix_1); }).length == 0) {
mailArray.push({
mail: line.substr(0, ix_1),
name: line.substr(ix_1 + 1)
});
curCounter++;
}
else {
console.error("Skipping " + line.substr(0, ix_1) + ": Already sent");
}
}
else {
console.error("Error parsing mail on line " + lineCounter + ": Syntax Error. Missing ;");
}
}
});
rl.on('close', function (line) {
console.log("Read " + curCounter + " adresses for section " + currSection + "\n" + mailArray.length + " mails read!");
resolve(mailArray);
});
});
}
exports.parseMails = parseMails;

74
dist/src/vault.js vendored
View File

@@ -60,11 +60,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecureVault = void 0;
var crypto = __importStar(require("crypto"));
var uuid = __importStar(require("uuid"));
var path_1 = __importDefault(require("path"));
var fs = __importStar(require("fs"));
var crypto_1 = require("crypto");
var vaultVersion = 'v1.2';
var SecureVault = (function () {
function SecureVault(publicKey, privateKey) {
this.storage = [];
this.safe = {
items: [],
publicKey: publicKey ? fs.readFileSync(path_1.default.resolve(publicKey)) : undefined,
@@ -75,7 +78,7 @@ var SecureVault = (function () {
}
SecureVault.prototype.pushData = function (data) {
return __awaiter(this, void 0, void 0, function () {
var txtData, key, iv, cipher, encrypted, buffer, asym_encrypted;
var txtData, key, iv, cipher, encrypted, buffer, asym_encrypted, u;
return __generator(this, function (_a) {
txtData = JSON.stringify(data);
key = crypto.randomBytes(32);
@@ -88,27 +91,45 @@ var SecureVault = (function () {
throw new Error("Public Key not found");
}
asym_encrypted = crypto.publicEncrypt(this.safe.publicKey, buffer);
u = uuid.v4();
this.safe.items.push({
u: u,
d: encrypted.toString('hex'),
k: asym_encrypted.toString("base64"),
iv: iv.toString('hex')
});
return [2];
return [2, u];
});
});
};
SecureVault.prototype.saveData = function (path) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
fs.writeFileSync(path, JSON.stringify(this.safe.items));
fs.writeFileSync(path, JSON.stringify({
version: vaultVersion,
vault: this.safe.items,
storage: this.storage
}));
return [2];
});
});
};
SecureVault.prototype.loadData = function (path) {
return __awaiter(this, void 0, void 0, function () {
var loaded;
return __generator(this, function (_a) {
this.safe.items = JSON.parse(fs.readFileSync(path, 'utf8'));
loaded = JSON.parse(fs.readFileSync(path, 'utf8'));
switch (loaded.version) {
case 'v1.1':
this.safe.items = loaded.vault;
break;
case 'v1.2':
this.safe.items = loaded.vault;
this.storage = loaded.storage;
break;
default:
console.error("Unknown or unsupported vault file version: " + loaded.version);
}
return [2];
});
});
@@ -151,6 +172,51 @@ var SecureVault = (function () {
fs.writeFileSync(publicKeyDir, publicKey);
});
};
SecureVault.prototype.pushStorage = function (tag, data) {
if (vaultVersion !== 'v1.2') {
throw new Error("Storage not supported in " + vaultVersion);
}
else {
var objJsonStr = JSON.stringify(data);
var objJsonB64 = Buffer.from(objJsonStr).toString("base64");
this.storage.push({
u: uuid.v4(),
d: objJsonB64,
t: tag
});
}
};
SecureVault.prototype.setStorage = function (suuid, data) {
if (vaultVersion !== 'v1.2') {
throw new Error("Storage not supported in " + vaultVersion);
}
else {
var objJsonStr = JSON.stringify(data);
var objJsonB64 = Buffer.from(objJsonStr, "utf8").toString("base64");
this.storage.filter(function (el) { return el.u == suuid; })[0].d = objJsonB64;
}
};
SecureVault.prototype.getStorage = function (suuid) {
if (vaultVersion !== 'v1.2') {
throw new Error("Storage not supported in " + vaultVersion);
}
else {
var data = this.storage.filter(function (el) { return el.u == suuid; })[0];
var objJsonB64 = new Buffer(data.d, 'base64');
return JSON.parse(objJsonB64.toString('utf8'));
}
};
SecureVault.prototype.findStorage = function (tag) {
if (vaultVersion !== 'v1.2') {
throw new Error("Storage not supported in " + vaultVersion);
}
else {
return this.storage.filter(function (el) { return el.t == tag; });
}
};
SecureVault.prototype.clearVault = function () {
this.safe.items = [];
};
return SecureVault;
}());
exports.SecureVault = SecureVault;

View File

@@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"exec": "ts-node index.ts",
"build": "rm bin/* -f && rm dist/* -Rf && tsc && pkg . --out-path bin --targets linux,macos,win"
},
"author": "",