{"id":15789922,"url":"https://github.com/gettocat/filtergraphy","last_synced_at":"2025-03-31T18:27:27.985Z","repository":{"id":57236066,"uuid":"434979605","full_name":"gettocat/filtergraphy","owner":"gettocat","description":"Message exchange based on bloom filters and EC e2e cryptography","archived":false,"fork":false,"pushed_at":"2022-02-23T16:29:31.000Z","size":607,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-05T22:04:13.279Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gettocat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-04T18:32:26.000Z","updated_at":"2021-12-04T18:50:38.000Z","dependencies_parsed_at":"2022-08-23T15:50:58.928Z","dependency_job_id":null,"html_url":"https://github.com/gettocat/filtergraphy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Ffiltergraphy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Ffiltergraphy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Ffiltergraphy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Ffiltergraphy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gettocat","download_url":"https://codeload.github.com/gettocat/filtergraphy/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246516948,"owners_count":20790325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-04T22:04:12.640Z","updated_at":"2025-03-31T18:27:27.965Z","avatar_url":"https://github.com/gettocat.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FilterCryptography\n\n## Intro\n\nThis package implements the functionality of secure chat without transferring information about the sender and recipients.\n\n### Algorithm\n\nAlgorithm 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. \n\nMedia 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. \n\nАlgorithm has several options for interaction, described below.\n\n## p2p (person to person)\n\nFor all examples have:\nAlice (A) with private and public key\nBob (B) with private and public key\n\nX = secret key, created by diffi-hellman algorithm:\nPseudocode:\n\n`X1 = (publicKeyAlice, privateKeyBob), X2 = (publicKeyBob, privateKeyAlice), X1 == X2 == X`\n\n`EncryptedMessage = encrypt(X, message);`\n\n`bloomFilterAliceWithSalt = bloomFilter(randomBytes + publicKeyAlice)`\n`bloomFilterBobWithSalt = bloomFilter(randomBytes + publicKeyBob)`\n\n\n### keyfrom-keyto\n\n`Payload = publicKeyAlice|publicKeyBob|EncryptedMessage`\n\n### filterfrom-keyto\n\n`Payload = bloomFilterAliceWithSalt|publicKeyBob|EncryptedMessage`\n\n\n### keyfrom-filterto\n\n`Payload = publicKeyAlice|bloomFilterBobWithSalt|EncryptedMessage`\n\n### filterfrom-filterto (f2f)\n\n`Payload = bloomFilterAliceWithSalt|bloomFilterBobWithSalt|EncryptedMessage`\n\n### tempkey\n\nUsed 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.\n\n`Payload = publicKeyAlice|bloomFilterBobWithSalt|SwitchMessage|SwitchKeyAdditionalData`\n`SwitchMessage = encrypt(X,oldkey|newkey)`\n`SwitchKeyAdditionalData = EncryptedData`\n\n### hellopublickey\n\nUsed for media channels. Bob create extendedPublicKey with xpub. Alice use this xpub for create new dialog.\nAlice 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 \n\n`Payload = publicKeyAlice|bloomFilterBobWithSalt|SwitchHelloPublicKeyMessage`\n`SwitchHelloPublicKeyMessage = encrypt(X,oldkey|newkey|path)`\n`SwitchKeyAdditionalData = EncryptedData`\n\n## Media (media to group of persons)\n\nAll info about media save in cloud db. Information contains media type, it can me public and private.\n\n### public media\n\nPublic 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\n\n### private media\n\nPrivate 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.\n\n## Events\n\nAll necessary methods are implemented as events that need to be overridden. Below is a list of events with parameters and the necessary functionality.\n\n```js\n\n/*decrypt events*/\napp.on('addDialog', (localkey, externalkey, callback, dialogName) =\u003e {\n    //add dialog to local db\n    callback();\n})\n\napp.on('removeDialog', (localkey, externalkey, callback) =\u003e {\n    //remove existing dialog to local db\n    callback();\n})\n\napp.on('getAllLocalPublicKeysList', (callback) =\u003e {\n    //get all public keys from keystore\n    callback(list);\n})\n\napp.on('getMediaKeys', (callback) =\u003e {\n    //get all my media public keys \n    callback([])\n})\n\napp.on('getKeyInfo', (key, callback) =\u003e {\n    //get dialog by localkey==key, and keystore of key\n    callback(dialog, keystore);\n})\n\napp.on('getDialogByExternalKey', (externalkey, callback) =\u003e {\n    //find dialog by externalkey\n    callback();\n})\n\n/*encrypt events*/\napp.on('getKeystoreByMeta', (context, version, callback) =\u003e {\n    //fild keystore by context.localkey\n});\n\n/*keys events*/\napp.on('seedAccess', (callback) =\u003e {\n    //get access to seed phrase, use new app.seed(\"mnemonic\")\n    callback(new app.seed(mnemonic));\n})\n\napp.on('getLastIndex', (callback) =\u003e {\n    //get last used key index\n    callback(k++);\n})\n\napp.on('getLastPublicIndex', (callback) =\u003e {\n    //get last used public key index\n    callback(pk++)\n})\n\napp.on('saveNewKey', (type, keystore, callback) =\u003e {\n    //save key to database, return keystore to callback\n    callback(keystore);\n})\n\n//media:\napp.on('getMediaInfo', (nameOrKey, callback) =\u003e {\n    \n    //find media by nameOrKey and return info\n    callback(mediaLocalInfo);\n})\n\napp.on('saveMediaInfo', (data, callback) =\u003e {\n    //save data to localdb\n    callback(data)\n})\n\napp.on('getAllFollowedMediaKeys', (callback) =\u003e {\n    //get add my media dialogs localkey\n    callback([myMediaKey1, myMediaKey2])\n})\n\napp.on('addMediaDialog', (localkey, mediaName, callback) =\u003e {\n    //add new dialog with media\n    callback({ localkey, externalKey: mediaName });\n})\n\napp.on('removeMediaDialog', (localkey, mediaName, callback) =\u003e {\n    //remove dialog with media\n    callback();\n})\n\napp.on('getDialogWithMedia', (mediaName, callback) =\u003e {\n    //find dialog by media name\n    callback(dialogsWithMedia[mediaName])\n})\n\napp.on('getMediaFollowers', (mediaPubKey, callback) =\u003e {\n    //get all followers from my private media\n    callback(followers[mediaPubKey]);\n})\n\napp.on('addFollower', (localkey, followerkey, callback) =\u003e {\n    //add follower to localtable of private media\n    if (!followers[localkey])\n        followers[localkey] = [];\n    followers[localkey].push(followerkey);\n    callback()\n})\n\napp.on('removeFollower', (localkey, followerkey, callback) =\u003e {\n    //remove follower key from private media db\n    followers[localkey].splice(followers[localkey].indexOf(followerkey), 1);\n    callback()\n})\n\n//net\napp.on('NET:getMediaInfo', (name, callback) =\u003e {\n    //get media info from network\n    callback(mediaNetInfo[name])\n})\n\napp.on('NET:saveMediaInfo', (name, data, callback) =\u003e {\n    //save media info to network\n    mediaNetInfo[name] = data;\n    callback(mediaNetInfo[name]);\n})\n\napp.on('NET:setFollowState', (mediaName, followerState, callback) =\u003e {\n    //set follower to follower table of public media\n    callback();\n})\n\napp.on('NET:getFollowState', (mediaName, followerKey, callback) =\u003e {\n    //get follower from follower table of public media\n    callback(mediaNetFollowStateInfo[mediaName][followerKey]);\n})\n\napp.on('NET:getMempoolHistory', (callback) =\u003e {\n    //get last N tx times from mempool\n    callback([]);\n});\n\n\napp.on('NET:sendmempool', (data, callback) =\u003e {\n    //send data to mempool\n    callback()\n});\n\n/**\n * another events\n */\n\napp.on('saveMessage', (dialog, content, options, callback) =\u003e {\n    //save message to localdb\n    callback({ hash: options.hash });\n})\n\napp.on('follower', (pubkey, followerKey) =\u003e {\n    //info about new follower to private media (without callback)\n})\n\napp.on('notfollower', () =\u003e {\n    //info about unfollow of follower to private media (without callback)\n})\n\napp.on('msg', (msg) =\u003e {\n    //info about new message from network (without callback)\n});\n```\n\n\n\n## Initialization\n\n```js\n    const APP = require('filtergraphy')\n    let app = new APP();\n```\n\n## Methods\n\n```js\napp.createKeyPair(type)\n    .then(keystore=\u003e{\n        \n    })\n```\n\n```js\napp.createPublicKeyPair()\n    .then(keystore=\u003e{\n        //creates keystore with keystore.xpub key (for hellopublickey and medias)\n    })\n```\n\n```js\napp.addDialog(localkey, externalkey)\n    .then(dialog=\u003e{\n        //creates new private dialog\n    })\n```\n\n```js\napp.removeDialog(localkey, externalkey)\n    .then(()=\u003e{\n        //remove private dialog\n    })\n```\n\n```js\napp.addMediaDialog(localkey, mediaName)\n    .then(()=\u003e{\n        //add dialog with media dialog \n    })\n```\n\n```js\napp.removeMediaDialog(localkey, mediaName)\n    .then(()=\u003e{\n        //remove dialog with media dialog\n    })\n```\n\n```js\napp.addMediaFollower(localKey, followerKey)\n    .then(()=\u003e{\n        //add followers to PRIVATE_MEDIA media\n    })\n```\n\n```js\napp.removeMediaFollower(localKey, followerKey)\n    .then(()=\u003e{\n        //remove followers to PRIVATE_MEDIA media\n    })\n```\n\n```js\n//creates new media and add it to media table.\n//name is a `username` of media\n//type can me 'MEDIA_PUBLIC' or 'MEDIA_PRIVATE'\napp.createMedia(name, type)\n    .then(()=\u003e{\n        //remove followers to PRIVATE_MEDIA media\n    })\n```\n\n```js\n//get info about media\napp.getMedia(media_name)\n    .then(media=\u003e{\n        \n    })\n```\n\n```js\n//only for media.type == MEDIA_PUBLIC\napp.getFollowState(mediaName, followerKey)\n    .then(followState=\u003e{\n        //returns info about follower of media  \n    })\n```\n\n```js\n//create new keystore and follow to media\napp.follow(mediaName)\n```\n\n```js\n//get follow keystore and unfollow from media\napp.unfollow(mediaName)\n```\n\n```js\n//broadcast buffer to all followers of media\napp.mediaBroadcast(mediaName, buffer)\n```\n\n\n```js\n/**\n * version types:\n * App.Crypto.FILTERTO\n * App.Crypto.FILTERFROMFILTERTO\n * App.Crypto.KEYTO\n * App.Crypto.KEYFROMKEYTO\n * App.Crypto.MEDIA\n * App.Crypto.HELLOPUBLICKEY\n * App.Crypto.TEMPKEY\n * \n * \n * context = {localkey, externalkey}\n * \n * For version App.Crypto.HELLOPUBLICKEY\n * context = { externalkeyWithDerive: xpub }\n */\napp.encrypt(context, buffer, version)\n    .then(encryptedBuffer=\u003e{\n        \n    })\n```\n\n```js\napp.decrypt(encryptedBuffer)\n    .then(result=\u003e{\n\n        //filter from-to, key from-to etc..\n        result = {\n            key: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n            dialog: {\n                localkey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n                externalkey: '02178d4517d71b8ed15544e377000f832725b6ebd1dfda72e2a01af2467d30395e'\n            },\n            keystore: {\n                publicKey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n                privateKey: '30068c1e860fac3e3aa16083310d3e6b1d061e53da16b08b100621c4f11796ec',\n                path: \"0'/0'/2\",\n                index: 2,\n                keyType: 'private_dialog'\n            },\n            content: Buffer,\n            meta: {\n                from: '02178d4517d71b8ed15544e377000f832725b6ebd1dfda72e2a01af2467d30395e',\n                to: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n                version: 2\n            }\n        }\n        \n        //tempkey result:\n        result = {\n            key: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n            dialog: {\n                localkey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n                externalkey: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb'\n            },\n            keystore: {\n                publicKey: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n                privateKey: 'xxxx',\n                path: \"0'/0'/2\",\n                index: 2,\n                keyType: 'private_dialog'\n            },\n            content: Buffer,\n            meta: {\n                from: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb',\n                to: '03e27cfa304366c849be09cac09c0e03baf3f371291eb4adc2a36a2a9a002ae83c',\n                version: 11,\n                switch: SwitchKey {\n                typename: 'SwitchKey',\n                oldkey: [PublicKey],\n                newkey: [PublicKey]\n                }\n            },\n            raw: Buffer\n        }\n\n        //hellopublickey result:\n        result = {\n            dialog: {\n                externalkey: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb',\n                localkey: '02da762b0cf81bb366bb6d83349cdc631626f1457bf61c7ab1080c8d4163ec885c'\n            },\n            keystore: {\n                publicKey: '02da762b0cf81bb366bb6d83349cdc631626f1457bf61c7ab1080c8d4163ec885c',\n                privateKey: 'xxxxx',\n                path: '0/0/1',\n                index: 1,\n                keyType: 'public_keypair',\n                xpub: 'xpub6H3ZCjPj4Dsam3gx8VA29NRMqfujTXM8Wefuoa7T2o4fmRsPTmURiiCF2RXvM2g3nMMyFZRgab3JheQnAay7g1NkAP8JasxATGy7hjYBA5z'\n            },\n            content: Buffer,\n            meta: {\n                from: '02ff3efab1d0916490814cf03bc6e4d4b6125e14a2a41b2f04766a202209ba4bdb',\n                to: '02da762b0cf81bb366bb6d83349cdc631626f1457bf61c7ab1080c8d4163ec885c',\n                version: 10,\n                switchHelloPublicKey: SwitchHelloPublicKey {\n                    typename: 'SwitchHelloPublicKey',\n                    oldkey: [PublicKey],\n                    newkey: [PublicKey],\n                    path: [HelloPublicKeyPath]\n                }\n            }\n        }\n\n\n\n    })  \n    .catch(message=\u003e{\n        //can not decrypt message\n    })\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgettocat%2Ffiltergraphy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgettocat%2Ffiltergraphy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgettocat%2Ffiltergraphy/lists"}