consento-org / crypto

Crypto primitives for the consento workflow
MIT License
5 stars 1 forks source link

New Permission System #11

Open martinheidegger opened 3 years ago

martinheidegger commented 3 years ago

Problem We need a permission system that allows users to connect multiple devices that they control to one, equal, space. To fix this, following concept:

Permission system based on hypercores

Goal:

Non-Goal:

Vocabulary

Usage

const { createGroup, Member, Group } = require('@consento/crypto')

const { member: alice } = await createGroup({ storage: ram() })
alice.public // Public Connection object, known to other clients that can be used to read data from alice or send data to alice
alice.private // Private Connection object, known to alice that can be used to read data to alice or send data to the group
alice.groupReader // Reader to read data from the group
alice.toJSON() // { public, private, group } = The entire identity of client
alice.groupState // A runtime-only reduced set of information that contains information about all members and requests, mobx object for real-time observation
alice.replicate() // Opens a replication feed that allows to replicate the group's status (hypercore based)
alice.version // Vector number: length of all `known-client-feeds` together with the length for the `group-feed` 

alice.groupState.sync = { [alice.public.connectionKey]: 1 } // known version for every other member

const bob = new Member({ groupReader: alice.groupReader, /* can be restored using the private, public information of .toJSON() */, storage: ram() })
alice.groupState.member[bob.public.connectionKey] // undefined - not added unknown monster

const requestId = await alice.addSibling(bob.public.connectionKey) // Adds a new request to alice's client-feed and signs immediately the output, because only alice is a sibling.
await alice.removeSibling(bob.public.connectionKey) // Adds a new request to alice's client-feed but 
alice.groupState.member[bob.public.connectionKey] // 'confirming-add' - confirmed by alice but since bob doesn't know about it yet, we don't continue on it
alice.groupState.request[requestId] // 'pending'

await alice.abort(requestId) // Means to abort a previous request, can only be done by the peer that requested this action.

// Synching two clients
const stream = alice.replicate()
stream.pipe(bob.replicate()).pipe(stream)

new Group({ reader: alice.groupReader, storage: ram() }) // equals to alice.groupState - Allows to Replicate a group and read data to it, but not interact with the group.

hypercore(alice.groupReader.verifyKey) // the hypercore with all the "writes" - `group-feed`
hypercore(alice.public.output.verifyKey) // the hypercore for all the requests that alice created (content can not be read without decryptKey) - `client-feed`

const bobToAlice = alice.ephemeral.boxes[concat(bob.public.connectionKey, alice.public.connectionKey)] // Data encrypted for bob to alice 
const aliceToBob = alice.ephemeral.boxes[concat(alice.public.connectionKey, bob.public.connectionKey)] // Data encrypted for alice to bob

bobToAlice.shard
bobToAlice.signatures[requestId] // Signature for a given request
alice.ephemeral.version // Version for the data in the boxes - while a sync is running this may be different than `alice.version`

Security and Data persistence

A client can only append to the group-feed with the group-secret. As long as a member is the only member of group, this secret will be stored by that client, but with the second client added the group-secret will be split into shards, to recreate the group-secret we need a threshold of n=amount(members) - 1 different shards. (exception: with 1 and 2 where we need all shards)

To allow the deletion of a member we need n - 1 shards, else the last member is going to be tricky. If we would allow n - 2 shards it may be the case that the writing member doesn't have the latest version of the group-feed which would cause a data integrity error.

During a sync process, when a member notices a new pending-request, it signs the the request and send it together with shards to each other member. Once a member received enough shards and valid signatures, it restores the group-sign-key and uses it to write the request, with the signatures, to the group-feed.

That member then has to "forget" about the other shards, and - if the members have changed - create a new set of shards for each new siblings and start distributing them as ephemeral data to all other members.

The integrity of the group-feed can be verified by going through the signatures of every request.

If the integrity of a feed-entry can not be verified, the changes will not be accepted as in-sync and the member that wrote the change will be automatically requested to be removed (faulty member). If an entry does not even have a signature, it is assumed that the group-feed-secret has been leaked. The peer sending the new entry will be put on a black-list, the feed will not be further processed and a recommendation is given to the user to create a new group Note: the black-listing makes sure that the integrity stays intact and the system keeps on working, but it is still open to a ddos-attack

A risk factor is if a member or known-members key is lost and may be repurposed for forking the group-feed. To avoid that members need to use stream-ciphers to store data when writing to the client-feeds and clients only store the last vector on their device. If it happens to be compromised they can not create a wrong feed.

Each client needs to always store their own shard, the group-feed and the date of all known-clients. This is done to preserve the history of the operations and make the whole log audit-able.

The ephemeral data needs to also be stored, but only until the member has shared a confirmed-receipt for the arrival.

The sharing of ephemeral data needs to be in steps: Alice sends ephemeral data to Bob; Bob looks if they can do anything with that ephemeral data and only if they can't Bob also shares their ephemeral data with Alice. This is done to prevent parallel writes to the group-feed.

martinheidegger commented 3 years ago

@RangerMauve and I just had an offline call. - I will need to rethink the specification. Rethought.

martinheidegger commented 3 years ago

Updated the proposal for the use of signvectors and signing in feeds.

dkastl commented 3 years ago

The Hyperledger people eventually call client an agent. Is client something used in hypercores?

RangerMauve commented 3 years ago

agent seems like a good name. 👍

dkastl commented 3 years ago

I think other terms are good and the meaning of group and member here is probably exactly what we imagine it should be.

RangerMauve commented 3 years ago

Writing notes about this https://hackmd.io/6ApfbzeSRC6Hw1nnez6vLQ?edit

cblgh commented 3 years ago

perhaps also of interest @martinheidegger (if anything as related work):

martinheidegger commented 3 years ago

Note: to follow the current progress on the permission system: https://github.com/consento-org/permissions-system-sim/pull/1