FilterCryptography
Intro
This package implements the functionality of secure chat without transferring information about the sender and recipients.
Algorithm
Algorithm assumes asymmetric encryption with the ability to hide the sender and recipient of the message. The algorithm has the ability to exchange messages between users, as well as organize a media channel that allows you to broadcast messages to channel subscribers without revealing the recipients and the sender.
Media channels can be of two types - public and private. Public media assumes an open list of subscribers stored in the cloud, and private - a closed one, in which only the media stores the list of subscribers.
Аlgorithm has several options for interaction, described below.
p2p (person to person)
For all examples have:
Alice (A) with private and public key
Bob (B) with private and public key
X = secret key, created by diffi-hellman algorithm:
Pseudocode:
X1 = (publicKeyAlice, privateKeyBob), X2 = (publicKeyBob, privateKeyAlice), X1 == X2 == X
EncryptedMessage = encrypt(X, message);
bloomFilterAliceWithSalt = bloomFilter(randomBytes + publicKeyAlice)
bloomFilterBobWithSalt = bloomFilter(randomBytes + publicKeyBob)
keyfrom-keyto
Payload = publicKeyAlice|publicKeyBob|EncryptedMessage
filterfrom-keyto
Payload = bloomFilterAliceWithSalt|publicKeyBob|EncryptedMessage
keyfrom-filterto
Payload = publicKeyAlice|bloomFilterBobWithSalt|EncryptedMessage
filterfrom-filterto (f2f)
Payload = bloomFilterAliceWithSalt|bloomFilterBobWithSalt|EncryptedMessage
tempkey
Used for secretly change the public key of the opposite side. Alice sending message with oldkey and Bob after receive this message change dialog externalkey (PublicKeyAlice) to newkey.
Payload = publicKeyAlice|bloomFilterBobWithSalt|SwitchMessage|SwitchKeyAdditionalData
SwitchMessage = encrypt(X,oldkey|newkey)
SwitchKeyAdditionalData = EncryptedData
hellopublickey
Used for media channels. Bob create extendedPublicKey with xpub. Alice use this xpub for create new dialog.
Alice makes temp key, and create externalkey by derive from xpub. Then send to xpub key message with new key (like in tempkey) and path of derived key. Bob received this message, create derived publicKey from path and xpub, and create dialog with tempkey, derivedkey
Payload = publicKeyAlice|bloomFilterBobWithSalt|SwitchHelloPublicKeyMessage
SwitchHelloPublicKeyMessage = encrypt(X,oldkey|newkey|path)
SwitchKeyAdditionalData = EncryptedData
Media (media to group of persons)
All info about media save in cloud db. Information contains media type, it can me public and private.
public media
Public media uses a cloud db to store a list of subscribers, which is stored openly and each network member can count the number of subscribers and their public keys. Messages are sent openly without encryption
private media
Private media store the list of subscribers locally, the subscription is done via encrypted messages. The number of subscribers and their list cannot be read. Messages are sent in encrypted form.
Events
All necessary methods are implemented as events that need to be overridden. Below is a list of events with parameters and the necessary functionality.
app.on('addDialog', (localkey, externalkey, callback, dialogName) => {
callback();
})
app.on('removeDialog', (localkey, externalkey, callback) => {
callback();
})
app.on('getAllLocalPublicKeysList', (callback) => {
callback(list);
})
app.on('getMediaKeys', (callback) => {
callback([])
})
app.on('getKeyInfo', (key, callback) => {
callback(dialog, keystore);
})
app.on('getDialogByExternalKey', (externalkey, callback) => {
callback();
})
app.on('getKeystoreByMeta', (context, version, callback) => {
});
app.on('seedAccess', (callback) => {
callback(new app.seed(mnemonic));
})
app.on('getLastIndex', (callback) => {
callback(k++);
})
app.on('getLastPublicIndex', (callback) => {
callback(pk++)
})
app.on('saveNewKey', (type, keystore, callback) => {
callback(keystore);
})
app.on('getMediaInfo', (nameOrKey, callback) => {
callback(mediaLocalInfo);
})
app.on('saveMediaInfo', (data, callback) => {
callback(data)
})
app.on('getAllFollowedMediaKeys', (callback) => {
callback([myMediaKey1, myMediaKey2])
})
app.on('addMediaDialog', (localkey, mediaName, callback) => {
callback({ localkey, externalKey: mediaName });
})
app.on('removeMediaDialog', (localkey, mediaName, callback) => {
callback();
})
app.on('getDialogWithMedia', (mediaName, callback) => {
callback(dialogsWithMedia[mediaName])
})
app.on('getMediaFollowers', (mediaPubKey, callback) => {
callback(followers[mediaPubKey]);
})
app.on('addFollower', (localkey, followerkey, callback) => {
if (!followers[localkey])
followers[localkey] = [];
followers[localkey].push(followerkey);
callback()
})
app.on('removeFollower', (localkey, followerkey, callback) => {
followers[localkey].splice(followers[localkey].indexOf(followerkey), 1);
callback()
})
app.on('NET:getMediaInfo', (name, callback) => {
callback(mediaNetInfo[name])
})
app.on('NET:saveMediaInfo', (name, data, callback) => {
mediaNetInfo[name] = data;
callback(mediaNetInfo[name]);
})
app.on('NET:setFollowState', (mediaName, followerState, callback) => {
callback();
})
app.on('NET:getFollowState', (mediaName, followerKey, callback) => {
callback(mediaNetFollowStateInfo[mediaName][followerKey]);
})
app.on('NET:getMempoolHistory', (callback) => {
callback([]);
});
app.on('NET:sendmempool', (data, callback) => {
callback()
});
app.on('saveMessage', (dialog, content, options, callback) => {
callback({ hash: options.hash });
})
app.on('follower', (pubkey, followerKey) => {
})
app.on('notfollower', () => {
})
app.on('msg', (msg) => {
});
Initialization
const APP = require('filtergraphy')
let app = new APP();
Methods
app.createKeyPair(type)
.then(keystore=>{
})
app.createPublicKeyPair()
.then(keystore=>{
})
app.addDialog(localkey, externalkey)
.then(dialog=>{
})
app.removeDialog(localkey, externalkey)
.then(()=>{
})
app.addMediaDialog(localkey, mediaName)
.then(()=>{
})
app.removeMediaDialog(localkey, mediaName)
.then(()=>{
})
app.addMediaFollower(localKey, followerKey)
.then(()=>{
})
app.removeMediaFollower(localKey, followerKey)
.then(()=>{
})
app.createMedia(name, type)
.then(()=>{
})
app.getMedia(media_name)
.then(media=>{
})
app.getFollowState(mediaName, followerKey)
.then(followState=>{
})
app.follow(mediaName)
app.unfollow(mediaName)
app.mediaBroadcast(mediaName, buffer)
app.encrypt(context, buffer, version)
.then(encryptedBuffer=>{
})
app.decrypt(encryptedBuffer)
.then(result=>{
result = {
key: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
dialog: {
localkey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
externalkey: '02178d4517d71b8ed15544e377000f832725b6ebd1dfda72e2a01af2467d30395e'
},
keystore: {
publicKey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
privateKey: '30068c1e860fac3e3aa16083310d3e6b1d061e53da16b08b100621c4f11796ec',
path: "0'/0'/2",
index: 2,
keyType: 'private_dialog'
},
content: Buffer,
meta: {
from: '02178d4517d71b8ed15544e377000f832725b6ebd1dfda72e2a01af2467d30395e',
to: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
version: 2
}
}
result = {
key: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
dialog: {
localkey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
externalkey: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb'
},
keystore: {
publicKey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
privateKey: 'xxxx',
path: "0'/0'/2",
index: 2,
keyType: 'private_dialog'
},
content: Buffer,
meta: {
from: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb',
to: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',
version: 11,
switch: SwitchKey {
typename: 'SwitchKey',
oldkey: [PublicKey],
newkey: [PublicKey]
}
},
raw: Buffer
}
result = {
dialog: {
externalkey: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb',
localkey: '02da762b0cf81bb366bb6d83349cdc631626f1457bf61c7ab1080c8d4163ec885c'
},
keystore: {
publicKey: '02da762b0cf81bb366bb6d83349cdc631626f1457bf61c7ab1080c8d4163ec885c',
privateKey: 'xxxxx',
path: '0/0/1',
index: 1,
keyType: 'public_keypair',
xpub: 'xpub6H3ZCjPj4Dsam3gx8VA29NRMqfujTXM8Wefuoa7T2o4fmRsPTmURiiCF2RXvM2g3nMMyFZRgab3JheQnAay7g1NkAP8JasxATGy7hjYBA5z'
},
content: Buffer,
meta: {
from: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb',
to: '02da762b0cf81bb366bb6d83349cdc631626f1457bf61c7ab1080c8d4163ec885c',
version: 10,
switchHelloPublicKey: SwitchHelloPublicKey {
typename: 'SwitchHelloPublicKey',
oldkey: [PublicKey],
newkey: [PublicKey],
path: [HelloPublicKeyPath]
}
}
}
})
.catch(message=>{
})