Merge pull request #42 from neuroscr/master

Minor whitelist QoL improvements
This commit is contained in:
Ryan Tharp 2020-06-10 18:54:57 -07:00 committed by GitHub
commit 43fcc89863
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 211 additions and 2 deletions

View File

@ -132,6 +132,11 @@ module.exports = (app, prefix) => {
ok = true;
}
// GET /token is valid, if you're passing a token...
if (req.method.toLowerCase() === 'get' && req.path.match(/^\/token/i)) {
ok = true;
}
// all loki endpoints are valid
if (req.path.match(/^\/loki\/v/)) {
ok = true;

146
lib.loki_crypt.js Normal file
View File

@ -0,0 +1,146 @@
const crypto = require('crypto');
const libsignal = require('libsignal');
const bb = require('bytebuffer');
/*
bufferFrom64
bufferTo64
bufferFromHex
bufferToHex
*/
const IV_LENGTH = 16;
const NONCE_LENGTH = 12;
const TAG_LENGTH = 16;
async function DHEncrypt(symmetricKey, plainText) {
const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const ciphertext = await libsignal.crypto.encrypt(
symmetricKey,
plainText,
iv
);
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
return ivAndCiphertext;
}
async function DHDecrypt(symmetricKey, ivAndCiphertext) {
const iv = ivAndCiphertext.slice(0, IV_LENGTH);
const ciphertext = ivAndCiphertext.slice(IV_LENGTH);
return libsignal.crypto.decrypt(symmetricKey, ciphertext, iv);
}
// used for proxy requests
const DHEncrypt64 = async (symmetricKey, plainText) => {
// generate an iv (web-friendly)
const iv = crypto.randomBytes(IV_LENGTH);
// encrypt plainText
const ciphertext = await libsignal.crypto.encrypt(
symmetricKey,
plainText,
iv
);
// create buffer
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
// copy iv into buffer
ivAndCiphertext.set(new Uint8Array(iv));
// copy ciphertext into buffer
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
// base64 encode
return bb.wrap(ivAndCiphertext).toString('base64');
}
// used for tokens
const DHDecrypt64 = async (symmetricKey, cipherText64) => {
// base64 decode
const ivAndCiphertext = Buffer.from(
bb.wrap(cipherText64, 'base64').toArrayBuffer()
);
// extract iv
const iv = ivAndCiphertext.slice(0, IV_LENGTH);
// extract ciphertext
const ciphertext = ivAndCiphertext.slice(IV_LENGTH);
// decode plaintext
return libsignal.crypto.decrypt(symmetricKey, ciphertext, iv);
}
function makeSymmetricKey(privKey, pubKey) {
const keyAgreement = libsignal.curve.calculateAgreement(
pubKey,
privKey,
);
//console_wrapper.log('makeSymmetricKey agreement', keyAgreement.toString('hex'))
// hash the key agreement
const hashedSymmetricKeyBuf = crypto.createHmac('sha256', 'LOKI').update(keyAgreement).digest()
return hashedSymmetricKeyBuf;
}
function encryptGCM(symmetricKey, plaintextEnc) {
// not on the node side
//const nonce = libsignal.crypto.getRandomBytes(NONCE_LENGTH);
const nonce = crypto.randomBytes(NONCE_LENGTH); // Buffer (object)
const cipher = crypto.createCipheriv('aes-256-gcm', symmetricKey, nonce);
const ciphertext = Buffer.concat([cipher.update(plaintextEnc), cipher.final()]);
const tag = cipher.getAuthTag()
const finalBuf = Buffer.concat([nonce, ciphertext, tag]);
return finalBuf;
}
function decryptGCM(symmetricKey, ivCiphertextAndTag) {
const nonce = ivCiphertextAndTag.slice(0, NONCE_LENGTH);
const ciphertext = ivCiphertextAndTag.slice(NONCE_LENGTH, ivCiphertextAndTag.byteLength - TAG_LENGTH);
const tag = ivCiphertextAndTag.slice(ivCiphertextAndTag.byteLength - TAG_LENGTH);
const decipher = crypto.createDecipheriv('aes-256-gcm', symmetricKey, nonce);
decipher.setAuthTag(tag);
return decipher.update(ciphertext, 'binary', 'utf8') + decipher.final();
}
// FIXME: bring in the multidevice support functions
// or maybe put them into a separate libraries
// reply_to if 0 not, is also required in adnMessage
async function getSigData(sigVer, privKey, noteValue, adnMessage) {
let sigString = '';
sigString += adnMessage.text.trim();
sigString += noteValue.timestamp;
if (noteValue.quote) {
sigString += noteValue.quote.id;
sigString += noteValue.quote.author;
sigString += noteValue.quote.text.trim();
if (adnMessage.reply_to) {
sigString += adnMessage.reply_to;
}
}
/*
sigString += [...attachmentAnnotations, ...previewAnnotations]
.map(data => data.id || data.image.id)
.sort()
.join();
*/
sigString += sigVer;
const sigData = Buffer.from(bb.wrap(sigString, 'utf8').toArrayBuffer());
const sig = await libsignal.curve.calculateSignature(privKey, sigData);
return sig.toString('hex')
}
module.exports = {
DHEncrypt,
DHDecrypt,
DHEncrypt64,
DHDecrypt64,
makeSymmetricKey,
encryptGCM,
decryptGCM,
getSigData,
}

View File

@ -3,16 +3,20 @@
//
// best we have a single entry point for all our common dialect to reduce set up in them
const bb = require('bytebuffer');
const libsignal = require('libsignal');
const storage = require('./storage');
const config = require('./lib.config');
const logic = require('./logic');
const dialect = require('./lib.dialect');
const loki_crypt = require('./lib.loki_crypt');
// Look for a config file
const disk_config = config.getDiskConfig();
storage.start(disk_config);
preflight = false
preflight = false;
const setup = (cache, dispatcher) => {
config.setup({ cache, storage });
@ -30,6 +34,46 @@ const setup = (cache, dispatcher) => {
console.log('rec', rec, 'meta', meta);
});
}
const addChannelMessage = (privKey, channelId) => {
return new Promise(resolve => {
dataAccess.addMessage({
channel_id: channelId,
text: 'system generated initial message',
machine_only: 0,
thread_id: 0,
userid: 1,
reply_to: 0,
is_deleted: 0,
created_at: new Date
}, async (msg, err) =>{
if (err) console.error('addChannelMessage err', err);
console.log('addChannelMessage msg', JSON.parse(JSON.stringify(msg)));
if (msg.id) {
var defaultObj = {
timestamp: parseInt(Date.now() / 1000),
};
/*
const sigData = getSigData(1, defaultObj, {
text: msg.text
});
const sig = await libsignal.curve.calculateSignature(privKey, sigData);
defaultObj.sigver = 1;
defaultObj.sig = sig.toString('hex');
*/
defaultObj.sig = await loki_crypt.getSigData(1, privKey, defaultObj, {
text: msg.text
});
defaultObj.sigver = 1;
dataAccess.addAnnotation('message', msg.id, 'network.loki.messenger.publicChat', defaultObj, function(rec, err, meta) {
console.log('created message 1!', JSON.parse(JSON.stringify(rec)));
resolve(err, msg);
});
}
});
});
}
if (!preflight) {
preflight = true
dataAccess.getChannel(1, {}, async (chnl, err, meta) => {
@ -71,11 +115,19 @@ const setup = (cache, dispatcher) => {
if (err2) console.error('get user 1 err', err2);
// if no user, create the user...
console.log('user', user);
var privKey, pubKey;
if (!user || !user.length) {
console.log('need to create user 1!');
// block until this is complete
user = await new Promise((resolve, rej) => {
dataAccess.addUser('root', '', function(user, err4, meta4) {
const ourKey = libsignal.curve.generateKeyPair();
privKey = ourKey.privKey;
pubKey = ourKey.pubKey;
var pubKeyhex = bb.wrap(ourKey.pubKey).toString('hex')
dataAccess.addUser(pubKeyhex, '', function(user, err4, meta4) {
if (err4) console.error('add user 1 err', err4);
// maybe some annotation to set the profile name...
// maybe a session icon?
resolve(user);
});
});
@ -98,6 +150,11 @@ const setup = (cache, dispatcher) => {
console.log('channel', chnl.id, 'created');
}
addChannelNote(chnl.id);
// only can do this if we just created the userid 1
if (privKey) {
console.log('need to create message 1!')
addChannelMessage(privKey, chnl.id);
}
});
});
});

View File

@ -35,6 +35,7 @@ module.exports = {
}
}
// by default everyone is not allowed
console.warn('logic:::permissions:::passesWhitelist - pubKey', pubKey, 'not whitelisted');
return false;
}
// in blacklist mode