- implement creating/loading an olm session for (userid, deviceid)
- get olm session for [(userid, deviceid), ...] (array so they can all go out in one /keys/claim call?)
- create if not exists
- claim one-time key with /keys/claim
- verify signature on key
- ??? what about inbound/outbound sessions? do they require multiple OlmSession objects?
- doesn't look like it, more like a way to start the session but once started (type=1), they are equivalent?
- for outbound, see file:///home/bwindels/Downloads/matrix-docs/End-to-End%20Encryption%20implementation%20guide%20%7C%20Matrix.org.html#starting-an-olm-session
- for inbound, see: file:///home/bwindels/Downloads/matrix-docs/End-to-End%20Encryption%20implementation%20guide%20|%20Matrix.org.html#handling-an-mroomencrypted-event
- so in this case, it would the session would be created as an outbound session.
- store pickled, index by curve25519 identity key?
- get from storage if exists and unpickle
- implement device tracking
- store users?
- needs_update (riot has status with 4 states: not tracked, pending download, downloading, up to date)
- set during sync for users that appear in device_lists.changed
- track which e2ee rooms a user is in? This so we don't need to load the member list when figuring out for which rooms a device changes has an effect. Maybe not yet needed here but we will need it to recalculate room trust. Perhaps we can also reuse the membership store if we have an index on (only) userid so we can ask with one query which rooms a user is in.
we'll need to pass an implementation of EventSender or something to SendQueue that does the actual requests to send a message, one implementation for non-e2ee rooms (upload attachment, send event OR redact, ...) and one for e2ee rooms that send the olm keys, etc ... encrypts the message before sending, reusing as much logic as possible. this will entail multiple sendScheduler.request slots, as we should only do one request per slot, making sure if we'd restart that steps completed in sending are stored so we don't run them again (advancing olm key, ...) or they are safe to rerun. The `E2eeEventSender` or so would then also be the thing that has a dependency on the memberlist for device tracking, which keeps the dependency tree clean (e.g. no setMembers on a class that does both e2ee and non-e2ee). We would also need to be able to encrypt non-megolm events with Olm, like 4S gossiping, etc ...
would be nice if we could expose the devices of a member as an observable list on the member
at the same time, we need to know if any member needs updating devices before sending a message... but this state would actually be kept on the member, so that works.
we do need to aggregate all the trust in a room though for shields... how would trust be added to this?
// the session we should use to encrypt with, or null if none exists
get outboundSession()
// should this be on device or something more specific to crypto? Although Device is specific to crypto ...
createOutboundSession()
// gets the matching session, or creates one if needed/allowed
async getInboundSessionForMessage()
e2ee/olm/OutboundSession
encrypt(type, content, txn) (same txn should be used that will add the message to pendingEvents, here used to advance ratchet)
e2ee/olm/InboundSession
decrypt(payload, txn)
e2ee/olm/Account
// for everything in crypto, we should have a method to persist the intent
createOTKs(txn)
// ... an another one to upload it, persisting that we have in fact uploaded it
uploadOTKs(txn)
DeviceList
writeSync(txn)
emitSync()
queryPending()
actually, we need two member stores:
- (Member) one for members per room with userid, avatar url, display name, power level, ... (most recent message timestamp)?
- (EncryptionUser) one per unique member id for e2ee, with device tracking status, and e2ee rooms the member is in? If we duplicate this over every room the user is in, we complicate device tracking.
the e2ee rooms an EncryptionUser is in needs to be notified of device (tracking) changes to update its trust shield. The fact that the device list is outdated or that we will need to create a new olm session when sending a message should not emit an event.
requirements:
- Members need to be able to exists without EncryptionUser
- Members need to be able to map to an EncryptionUser (by userId)
- Member needs to have trust property derived from their EncryptionUser, with updates triggered somehow in central point, e.g. by Room.emitSync
- also, how far do we want to take this central update point thing? I guess any update that will cascade in a room (summary) update ... so here adding a device would cascade into the room trust changing, which we want to emit from Room.emitSync.
- hmm, I wonder if it makes sense to do this over member, or rather expose a second ObservableMap on the room for EncryptionUser where we can monitor trust
- PROs separate observablemap:
- don't need to load member list to display shields of user in timeline ... this might be fine though as e2ee rooms tend to be smaller rooms, and this is only for the room that is open.
- CONs separate observablemap:
- more clunky api, would need a join operator on ObservableMap to join the trust and Member into one ObservableMap to be able to display member list.
- See if it is doable to sync e2ee rooms without having all their encryptionUsers and devices in memory:
- Be able to decrypt *without* having all EncryptionUsers of a room and their devices in memory, but rather use indices on storage to load just what we need. Finding a matching inbound olm session is something we need to think how to do best. We'll need to see this one.
- Be able to encrypt may require having all EncryptionUsers of a room and their devices in memory, as we typically only send in the room we are looking at (but not always, so forwarding an event, etc... might then require to "load" some of the machinery for that room, but that should be fine)
- Be able to send EncryptionUser updates *without* having all EncryptionUsers and their devices in memory
other:
- when populating EncryptionUsers for an e2ee room, we'll also populate Members as we need to fetch them from the server anyway.
- Members can also be populated for other reasons (showing member list in non-e2ee room)
we should adjust the session store to become a key value store rather than just a single value, we can split up in:
- syncData (filterId, syncToken, syncCount)
- serverConfig (/versions response)
- serialized olm account
so we don't have write all of that on every sync to update the sync token
new stores:
room-members
e2ee-users
e2ee-devices
inbound-olm-sessions
outbound-olm-sessions
//tdb:
inbound-megolm-sessions
outbound-megolm-sessions
we should create constants with sets of store names that are needed for certain use cases, like write timeline will require [timeline, fragments, inbound-megolm-sessions] which we can reuse when filling the gap, writing timeline sync, ...
main things to figure out:
- how to decrypt? what indices do we need? is it reasonable to do this without having all EncryptionUser/devices in memory?
- big part of this is how we can find the matching olm session for an incoming event/create a new olm session