diff --git a/README.md b/README.md index b9f209d..efad7a9 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ on the `sbot.box2` namespace: - `key` must be a buffer. The key can then be used for decrypting messages from the group, and if picked with `pickGroupWriteKey`, as a "recp" to encrypt messages to the group. Note that the keys are not persisted in this module. - `scheme` _String_ - scheme of that encryption key (optional, there is only one option at the moment which we default to) - `root` _MessageId_ the id of the `group/init` message -- `excludeGroupInfo(groupId, cb)`: Removes group info from a groupId, for instance if you or someone else has excluded you from one. Getting info about it will only return `{ excluded: true }`. Returns a promise if cb isn't provided. +- `excludeGroupInfo(groupId, cb)`: Removes the writeKey from a groupId and marks the group as excluded. Useful for instance if you or someone else has excluded you from the group. Getting info about the group will return the old group info minus the `writeKey` and plus an `excluded` field set to `true`. Returns a promise if cb isn't provided. - `listGroupIds({ live, excluded }) => PullStream`: Returns a pull stream of all groupIds whose messages you're able to decrypt. If `live` is true then it returns a pull stream with all previous but also all future group ids. If `excluded` is true then it returns only excluded groups (groups you've been excluded from) instead of only non-excluded groups. - `pickGroupWriteKey(groupId, pickedKey, cb)`: Picks one of the group's current read keys to be the group's write key. The picked key needs to exactly match one of the read keys. Returns a promise if cb isn't provided. - `groupId`: cloaked message id or uri encoded group id. diff --git a/format.js b/format.js index 6832abb..c9885b2 100644 --- a/format.js +++ b/format.js @@ -291,8 +291,9 @@ function makeEncryptionFormat() { const authorBFE = BFE.encode(authorId) const previousBFE = BFE.encode(opts.previous) - const groupKeys = keyring.group - .listSync() + const groups = keyring.group.listSync() + const excludedGroups = keyring.group.listSync({ excluded: true }) + const groupKeys = [...groups, ...excludedGroups] .map(keyring.group.get) .map((groupInfo) => groupInfo.readKeys) .flat() diff --git a/package.json b/package.json index 7c19c0c..a8f7179 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "pull-defer": "^0.2.3", "pull-stream": "^3.6.14", "ssb-bfe": "^3.7.0", - "ssb-keyring": "^5.3.0", + "ssb-keyring": "^5.3.2", "ssb-private-group-keys": "^1.1.1", "ssb-ref": "^2.16.0", "ssb-uri2": "^2.4.1" diff --git a/test/index.js b/test/index.js index 98b5018..8b789db 100644 --- a/test/index.js +++ b/test/index.js @@ -138,6 +138,45 @@ test('decrypt as group recipient', (t) => { }) }) +test('decrypt as group recipient, still works after exclusion', (t) => { + const box2 = Box2() + const keys = ssbKeys.generate(null, 'alice', 'buttwoo-v1') + + box2.setup({ keys }, () => { + const groupId = '%Lihvp+fMdt5CihjbOY6eZc0qCe0eKsrN2wfgXV2E3PM=.cloaked' + box2.addGroupInfo(groupId, { + key: Buffer.from( + '30720d8f9cbf37f6d7062826f6decac93e308060a8aaaa77e6a4747f40ee1a76', + 'hex' + ), + }) + + const opts = { + keys, + content: { type: 'post', text: 'super secret' }, + previous: null, + timestamp: 12345678900, + tag: buttwoo.tags.SSB_FEED, + hmacKey: null, + recps: [groupId, ssbKeys.generate(null, '2').id], + } + + const plaintext = buttwoo.toPlaintextBuffer(opts) + t.true(Buffer.isBuffer(plaintext), 'plaintext is a buffer') + + const ciphertext = box2.encrypt(plaintext, opts) + + box2.excludeGroupInfo(groupId, (err) => { + if (err) t.fail(err) + + const decrypted = box2.decrypt(ciphertext, { ...opts, author: keys.id }) + t.deepEqual(decrypted, plaintext, 'decrypted plaintext is the same') + + t.end() + }) + }) +}) + test('cannot decrypt own DM after we changed our own DM keys', (t) => { const box2 = Box2() const keys = ssbKeys.generate(null, 'alice', 'buttwoo-v1') diff --git a/test/tribes.js b/test/tribes.js index bf780bf..d762f13 100644 --- a/test/tribes.js +++ b/test/tribes.js @@ -12,6 +12,7 @@ const SecretStack = require('secret-stack') const caps = require('ssb-caps') const ref = require('ssb-ref') const pull = require('pull-stream') +const { keySchemes } = require('private-group-spec') function readyDir(dir) { rimraf.sync(dir) @@ -369,8 +370,12 @@ test('You can exclude info from a group', async (t) => { t.deepEquals( groupInfo, - { excluded: true }, - 'excluding group info just leaves excluded: true' + { + readKeys: [{ key: testkey, scheme: keySchemes.private_group }], + root: testRoot, + excluded: true, + }, + 'excluding group info removes writeKey and adds excluded: true' ) const listNotExcluded = await pull(