Merge pull request #42 from neuroscr/master
Minor whitelist QoL improvements
This commit is contained in:
commit
43fcc89863
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue