You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Merge pull request #587 from matrix-org/dbkr/inbound_sessions_to_cryptostore
Migrate inbound sessions to cryptostore
This commit is contained in:
@@ -10,6 +10,7 @@ import Promise from 'bluebird';
|
|||||||
import sdk from '../../../..';
|
import sdk from '../../../..';
|
||||||
import algorithms from '../../../../lib/crypto/algorithms';
|
import algorithms from '../../../../lib/crypto/algorithms';
|
||||||
import WebStorageSessionStore from '../../../../lib/store/session/webstorage';
|
import WebStorageSessionStore from '../../../../lib/store/session/webstorage';
|
||||||
|
import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js';
|
||||||
import MockStorageApi from '../../../MockStorageApi';
|
import MockStorageApi from '../../../MockStorageApi';
|
||||||
import testUtils from '../../../test-utils';
|
import testUtils from '../../../test-utils';
|
||||||
|
|
||||||
@@ -45,8 +46,9 @@ describe("MegolmDecryption", function() {
|
|||||||
|
|
||||||
const mockStorage = new MockStorageApi();
|
const mockStorage = new MockStorageApi();
|
||||||
const sessionStore = new WebStorageSessionStore(mockStorage);
|
const sessionStore = new WebStorageSessionStore(mockStorage);
|
||||||
|
const cryptoStore = new MemoryCryptoStore(mockStorage);
|
||||||
|
|
||||||
const olmDevice = new OlmDevice(sessionStore);
|
const olmDevice = new OlmDevice(sessionStore, cryptoStore);
|
||||||
|
|
||||||
megolmDecryption = new MegolmDecryption({
|
megolmDecryption = new MegolmDecryption({
|
||||||
userId: '@user:id',
|
userId: '@user:id',
|
||||||
|
|||||||
@@ -216,6 +216,41 @@ OlmDevice.prototype._migrateFromSessionStore = async function() {
|
|||||||
|
|
||||||
this._sessionStore.removeAllEndToEndSessions();
|
this._sessionStore.removeAllEndToEndSessions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inbound group sessions
|
||||||
|
const ibGroupSessions = this._sessionStore.getAllEndToEndInboundGroupSessionKeys();
|
||||||
|
if (Object.keys(ibGroupSessions).length > 0) {
|
||||||
|
let numIbSessions = 0;
|
||||||
|
await this._cryptoStore.doTxn(
|
||||||
|
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
||||||
|
// We always migrate inbound group sessions, even if we already have some
|
||||||
|
// in the new store. They should be be safe to migrate.
|
||||||
|
for (const s of ibGroupSessions) {
|
||||||
|
try {
|
||||||
|
this._cryptoStore.addEndToEndInboundGroupSession(
|
||||||
|
s.senderKey, s.sessionId,
|
||||||
|
JSON.parse(
|
||||||
|
this._sessionStore.getEndToEndInboundGroupSession(
|
||||||
|
s.senderKey, s.sessionId,
|
||||||
|
),
|
||||||
|
), txn,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(
|
||||||
|
"Failed to migrate session " + s.senderKey + "/" +
|
||||||
|
s.sessionId + ": " + e.stack || e,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
++numIbSessions;
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
"Migrated " + numIbSessions +
|
||||||
|
" inbound group sessions from session store",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this._sessionStore.removeAllEndToEndInboundGroupSessions();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -773,68 +808,62 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* store an InboundGroupSession in the session store
|
* Unpickle a session from a sessionData object and invoke the given function.
|
||||||
|
* The session is valid only until func returns.
|
||||||
*
|
*
|
||||||
* @param {string} senderCurve25519Key
|
* @param {Object} sessionData Object describing the session.
|
||||||
* @param {string} sessionId
|
* @param {function(Olm.InboundGroupSession)} func Invoked with the unpickled session
|
||||||
* @param {InboundGroupSessionData} sessionData
|
* @return {*} result of func
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype._saveInboundGroupSession = function(
|
OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) {
|
||||||
senderCurve25519Key, sessionId, sessionData,
|
|
||||||
) {
|
|
||||||
this._sessionStore.storeEndToEndInboundGroupSession(
|
|
||||||
senderCurve25519Key, sessionId, JSON.stringify(sessionData),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* extract an InboundGroupSession from the session store and call the given function
|
|
||||||
*
|
|
||||||
* @param {string} roomId
|
|
||||||
* @param {string} senderKey
|
|
||||||
* @param {string} sessionId
|
|
||||||
* @param {function(Olm.InboundGroupSession, InboundGroupSessionData): T} func
|
|
||||||
* function to call.
|
|
||||||
*
|
|
||||||
* @return {null} the sessionId is unknown
|
|
||||||
*
|
|
||||||
* @return {T} result of func
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @template {T}
|
|
||||||
*/
|
|
||||||
OlmDevice.prototype._getInboundGroupSession = function(
|
|
||||||
roomId, senderKey, sessionId, func,
|
|
||||||
) {
|
|
||||||
let r = this._sessionStore.getEndToEndInboundGroupSession(
|
|
||||||
senderKey, sessionId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (r === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = JSON.parse(r);
|
|
||||||
|
|
||||||
// check that the room id matches the original one for the session. This stops
|
|
||||||
// the HS pretending a message was targeting a different room.
|
|
||||||
if (roomId !== r.room_id) {
|
|
||||||
throw new Error(
|
|
||||||
"Mismatched room_id for inbound group session (expected " + r.room_id +
|
|
||||||
", was " + roomId + ")",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const session = new Olm.InboundGroupSession();
|
const session = new Olm.InboundGroupSession();
|
||||||
try {
|
try {
|
||||||
session.unpickle(this._pickleKey, r.session);
|
session.unpickle(this._pickleKey, sessionData.session);
|
||||||
return func(session, r);
|
return func(session);
|
||||||
} finally {
|
} finally {
|
||||||
session.free();
|
session.free();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract an InboundGroupSession from the crypto store and call the given function
|
||||||
|
*
|
||||||
|
* @param {string} roomId The room ID to extract the session for, or null to fetch
|
||||||
|
* sessions for any room.
|
||||||
|
* @param {string} senderKey
|
||||||
|
* @param {string} sessionId
|
||||||
|
* @param {*} txn Opaque transaction object from cryptoStore.doTxn()
|
||||||
|
* @param {function(Olm.InboundGroupSession, InboundGroupSessionData)} func
|
||||||
|
* function to call.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
OlmDevice.prototype._getInboundGroupSession = function(
|
||||||
|
roomId, senderKey, sessionId, txn, func,
|
||||||
|
) {
|
||||||
|
this._cryptoStore.getEndToEndInboundGroupSession(
|
||||||
|
senderKey, sessionId, txn, (sessionData) => {
|
||||||
|
if (sessionData === null) {
|
||||||
|
func(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we were given a room ID, check that the it matches the original one for the session. This stops
|
||||||
|
// the HS pretending a message was targeting a different room.
|
||||||
|
if (roomId !== null && roomId !== sessionData.room_id) {
|
||||||
|
throw new Error(
|
||||||
|
"Mismatched room_id for inbound group session (expected " +
|
||||||
|
sessionData.room_id + ", was " + roomId + ")",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._unpickleInboundGroupSession(sessionData, (session) => {
|
||||||
|
func(session, sessionData);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an inbound group session to the session store
|
* Add an inbound group session to the session store
|
||||||
*
|
*
|
||||||
@@ -853,100 +882,52 @@ OlmDevice.prototype.addInboundGroupSession = async function(
|
|||||||
sessionId, sessionKey, keysClaimed,
|
sessionId, sessionKey, keysClaimed,
|
||||||
exportFormat,
|
exportFormat,
|
||||||
) {
|
) {
|
||||||
const self = this;
|
await this._cryptoStore.doTxn(
|
||||||
|
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
||||||
|
/* if we already have this session, consider updating it */
|
||||||
|
this._getInboundGroupSession(
|
||||||
|
roomId, senderKey, sessionId, txn,
|
||||||
|
(existingSession, existingSessionData) => {
|
||||||
|
if (existingSession) {
|
||||||
|
console.log(
|
||||||
|
"Update for megolm session " + senderKey + "/" + sessionId,
|
||||||
|
);
|
||||||
|
// for now we just ignore updates. TODO: implement something here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* if we already have this session, consider updating it */
|
// new session.
|
||||||
function updateSession(session, sessionData) {
|
const session = new Olm.InboundGroupSession();
|
||||||
console.log("Update for megolm session " + senderKey + "/" + sessionId);
|
try {
|
||||||
// for now we just ignore updates. TODO: implement something here
|
if (exportFormat) {
|
||||||
|
session.import_session(sessionKey);
|
||||||
|
} else {
|
||||||
|
session.create(sessionKey);
|
||||||
|
}
|
||||||
|
if (sessionId != session.session_id()) {
|
||||||
|
throw new Error(
|
||||||
|
"Mismatched group session ID from senderKey: " +
|
||||||
|
senderKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
const sessionData = {
|
||||||
}
|
room_id: roomId,
|
||||||
|
session: session.pickle(this._pickleKey),
|
||||||
|
keysClaimed: keysClaimed,
|
||||||
|
forwardingCurve25519KeyChain: forwardingCurve25519KeyChain,
|
||||||
|
};
|
||||||
|
|
||||||
const r = this._getInboundGroupSession(
|
this._cryptoStore.addEndToEndInboundGroupSession(
|
||||||
roomId, senderKey, sessionId, updateSession,
|
senderKey, sessionId, sessionData, txn,
|
||||||
);
|
);
|
||||||
|
} finally {
|
||||||
if (r !== null) {
|
session.free();
|
||||||
return;
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
// new session.
|
|
||||||
const session = new Olm.InboundGroupSession();
|
|
||||||
try {
|
|
||||||
if (exportFormat) {
|
|
||||||
session.import_session(sessionKey);
|
|
||||||
} else {
|
|
||||||
session.create(sessionKey);
|
|
||||||
}
|
|
||||||
if (sessionId != session.session_id()) {
|
|
||||||
throw new Error(
|
|
||||||
"Mismatched group session ID from senderKey: " + senderKey,
|
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
const sessionData = {
|
|
||||||
room_id: roomId,
|
|
||||||
session: session.pickle(this._pickleKey),
|
|
||||||
keysClaimed: keysClaimed,
|
|
||||||
forwardingCurve25519KeyChain: forwardingCurve25519KeyChain,
|
|
||||||
};
|
|
||||||
|
|
||||||
self._saveInboundGroupSession(
|
|
||||||
senderKey, sessionId, sessionData,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
session.free();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a previously-exported inbound group session to the session store
|
|
||||||
*
|
|
||||||
* @param {module:crypto/OlmDevice.MegolmSessionData} data session data
|
|
||||||
*/
|
|
||||||
OlmDevice.prototype.importInboundGroupSession = async function(data) {
|
|
||||||
/* if we already have this session, consider updating it */
|
|
||||||
function updateSession(session, sessionData) {
|
|
||||||
console.log("Update for megolm session " + data.sender_key + "|" +
|
|
||||||
data.session_id);
|
|
||||||
// for now we just ignore updates. TODO: implement something here
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const r = this._getInboundGroupSession(
|
|
||||||
data.room_id, data.sender_key, data.session_id, updateSession,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (r !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// new session.
|
|
||||||
const session = new Olm.InboundGroupSession();
|
|
||||||
try {
|
|
||||||
session.import_session(data.session_key);
|
|
||||||
if (data.session_id != session.session_id()) {
|
|
||||||
throw new Error(
|
|
||||||
"Mismatched group session ID from senderKey: " + data.sender_key,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sessionData = {
|
|
||||||
room_id: data.room_id,
|
|
||||||
session: session.pickle(this._pickleKey),
|
|
||||||
keysClaimed: data.sender_claimed_keys,
|
|
||||||
forwardingCurve25519KeyChain: data.forwarding_curve25519_key_chain,
|
|
||||||
};
|
|
||||||
|
|
||||||
this._saveInboundGroupSession(
|
|
||||||
data.sender_key, data.session_id, sessionData,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
session.free();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -968,51 +949,68 @@ OlmDevice.prototype.importInboundGroupSession = async function(data) {
|
|||||||
OlmDevice.prototype.decryptGroupMessage = async function(
|
OlmDevice.prototype.decryptGroupMessage = async function(
|
||||||
roomId, senderKey, sessionId, body, eventId, timestamp,
|
roomId, senderKey, sessionId, body, eventId, timestamp,
|
||||||
) {
|
) {
|
||||||
const self = this;
|
let result;
|
||||||
|
|
||||||
function decrypt(session, sessionData) {
|
await this._cryptoStore.doTxn(
|
||||||
const res = session.decrypt(body);
|
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
||||||
|
this._getInboundGroupSession(
|
||||||
|
roomId, senderKey, sessionId, txn, (session, sessionData) => {
|
||||||
|
if (session === null) {
|
||||||
|
result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = session.decrypt(body);
|
||||||
|
|
||||||
let plaintext = res.plaintext;
|
let plaintext = res.plaintext;
|
||||||
if (plaintext === undefined) {
|
if (plaintext === undefined) {
|
||||||
// Compatibility for older olm versions.
|
// Compatibility for older olm versions.
|
||||||
plaintext = res;
|
plaintext = res;
|
||||||
} else {
|
} else {
|
||||||
// Check if we have seen this message index before to detect replay attacks.
|
// Check if we have seen this message index before to detect replay attacks.
|
||||||
// If the event ID and timestamp are specified, and the match the event ID
|
// If the event ID and timestamp are specified, and the match the event ID
|
||||||
// and timestamp from the last time we used this message index, then we
|
// and timestamp from the last time we used this message index, then we
|
||||||
// don't consider it a replay attack.
|
// don't consider it a replay attack.
|
||||||
const messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index;
|
const messageIndexKey = (
|
||||||
if (messageIndexKey in self._inboundGroupSessionMessageIndexes) {
|
senderKey + "|" + sessionId + "|" + res.message_index
|
||||||
const msgInfo = self._inboundGroupSessionMessageIndexes[messageIndexKey];
|
);
|
||||||
if (msgInfo.id !== eventId || msgInfo.timestamp !== timestamp) {
|
if (messageIndexKey in this._inboundGroupSessionMessageIndexes) {
|
||||||
throw new Error(
|
const msgInfo = (
|
||||||
"Duplicate message index, possible replay attack: " +
|
this._inboundGroupSessionMessageIndexes[messageIndexKey]
|
||||||
messageIndexKey,
|
);
|
||||||
|
if (
|
||||||
|
msgInfo.id !== eventId ||
|
||||||
|
msgInfo.timestamp !== timestamp
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"Duplicate message index, possible replay attack: " +
|
||||||
|
messageIndexKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._inboundGroupSessionMessageIndexes[messageIndexKey] = {
|
||||||
|
id: eventId,
|
||||||
|
timestamp: timestamp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionData.session = session.pickle(this._pickleKey);
|
||||||
|
this._cryptoStore.storeEndToEndInboundGroupSession(
|
||||||
|
senderKey, sessionId, sessionData, txn,
|
||||||
);
|
);
|
||||||
}
|
result = {
|
||||||
}
|
result: plaintext,
|
||||||
self._inboundGroupSessionMessageIndexes[messageIndexKey] = {
|
keysClaimed: sessionData.keysClaimed || {},
|
||||||
id: eventId,
|
senderKey: senderKey,
|
||||||
timestamp: timestamp,
|
forwardingCurve25519KeyChain: (
|
||||||
};
|
sessionData.forwardingCurve25519KeyChain || []
|
||||||
}
|
),
|
||||||
|
};
|
||||||
sessionData.session = session.pickle(self._pickleKey);
|
},
|
||||||
self._saveInboundGroupSession(
|
);
|
||||||
senderKey, sessionId, sessionData,
|
},
|
||||||
);
|
|
||||||
return {
|
|
||||||
result: plaintext,
|
|
||||||
keysClaimed: sessionData.keysClaimed || {},
|
|
||||||
senderKey: senderKey,
|
|
||||||
forwardingCurve25519KeyChain: sessionData.forwardingCurve25519KeyChain || [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._getInboundGroupSession(
|
|
||||||
roomId, senderKey, sessionId, decrypt,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1025,25 +1023,33 @@ OlmDevice.prototype.decryptGroupMessage = async function(
|
|||||||
* @returns {Promise<boolean>} true if we have the keys to this session
|
* @returns {Promise<boolean>} true if we have the keys to this session
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, sessionId) {
|
OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, sessionId) {
|
||||||
const s = this._sessionStore.getEndToEndInboundGroupSession(
|
let result;
|
||||||
senderKey, sessionId,
|
await this._cryptoStore.doTxn(
|
||||||
|
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
||||||
|
this._cryptoStore.getEndToEndInboundGroupSession(
|
||||||
|
senderKey, sessionId, txn, (sessionData) => {
|
||||||
|
if (sessionData === null) {
|
||||||
|
result = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomId !== sessionData.room_id) {
|
||||||
|
console.warn(
|
||||||
|
`requested keys for inbound group session ${senderKey}|` +
|
||||||
|
`${sessionId}, with incorrect room_id ` +
|
||||||
|
`(expected ${sessionData.room_id}, ` +
|
||||||
|
`was ${roomId})`,
|
||||||
|
);
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (s === null) {
|
return result;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const r = JSON.parse(s);
|
|
||||||
if (roomId !== r.room_id) {
|
|
||||||
console.warn(
|
|
||||||
`requested keys for inbound group session ${senderKey}|` +
|
|
||||||
`${sessionId}, with incorrect room_id (expected ${r.room_id}, ` +
|
|
||||||
`was ${roomId})`,
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1063,24 +1069,33 @@ OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, se
|
|||||||
OlmDevice.prototype.getInboundGroupSessionKey = async function(
|
OlmDevice.prototype.getInboundGroupSessionKey = async function(
|
||||||
roomId, senderKey, sessionId,
|
roomId, senderKey, sessionId,
|
||||||
) {
|
) {
|
||||||
function getKey(session, sessionData) {
|
let result;
|
||||||
const messageIndex = session.first_known_index();
|
await this._cryptoStore.doTxn(
|
||||||
|
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
||||||
|
this._getInboundGroupSession(
|
||||||
|
roomId, senderKey, sessionId, txn, (session, sessionData) => {
|
||||||
|
if (session === null) {
|
||||||
|
result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const messageIndex = session.first_known_index();
|
||||||
|
|
||||||
const claimedKeys = sessionData.keysClaimed || {};
|
const claimedKeys = sessionData.keysClaimed || {};
|
||||||
const senderEd25519Key = claimedKeys.ed25519 || null;
|
const senderEd25519Key = claimedKeys.ed25519 || null;
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
"chain_index": messageIndex,
|
"chain_index": messageIndex,
|
||||||
"key": session.export_session(messageIndex),
|
"key": session.export_session(messageIndex),
|
||||||
"forwarding_curve25519_key_chain":
|
"forwarding_curve25519_key_chain":
|
||||||
sessionData.forwardingCurve25519KeyChain || [],
|
sessionData.forwardingCurve25519KeyChain || [],
|
||||||
"sender_claimed_ed25519_key": senderEd25519Key,
|
"sender_claimed_ed25519_key": senderEd25519Key,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
);
|
||||||
return this._getInboundGroupSession(
|
},
|
||||||
roomId, senderKey, sessionId, getKey,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1088,37 +1103,24 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function(
|
|||||||
*
|
*
|
||||||
* @param {string} senderKey base64-encoded curve25519 key of the sender
|
* @param {string} senderKey base64-encoded curve25519 key of the sender
|
||||||
* @param {string} sessionId session identifier
|
* @param {string} sessionId session identifier
|
||||||
* @return {Promise<module:crypto/OlmDevice.MegolmSessionData>} exported session data
|
* @param {string} sessionData The session object from the store
|
||||||
|
* @return {module:crypto/OlmDevice.MegolmSessionData} exported session data
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype.exportInboundGroupSession = async function(senderKey, sessionId) {
|
OlmDevice.prototype.exportInboundGroupSession = function(
|
||||||
const s = this._sessionStore.getEndToEndInboundGroupSession(
|
senderKey, sessionId, sessionData,
|
||||||
senderKey, sessionId,
|
) {
|
||||||
);
|
return this._unpickleInboundGroupSession(sessionData, (session) => {
|
||||||
|
|
||||||
if (s === null) {
|
|
||||||
throw new Error("Unknown inbound group session [" + senderKey + "," +
|
|
||||||
sessionId + "]");
|
|
||||||
}
|
|
||||||
const r = JSON.parse(s);
|
|
||||||
|
|
||||||
const session = new Olm.InboundGroupSession();
|
|
||||||
try {
|
|
||||||
session.unpickle(this._pickleKey, r.session);
|
|
||||||
|
|
||||||
const messageIndex = session.first_known_index();
|
const messageIndex = session.first_known_index();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"sender_key": senderKey,
|
"sender_key": senderKey,
|
||||||
"sender_claimed_keys": r.keysClaimed,
|
"sender_claimed_keys": sessionData.keysClaimed,
|
||||||
"room_id": r.room_id,
|
"room_id": sessionData.room_id,
|
||||||
"session_id": sessionId,
|
"session_id": sessionId,
|
||||||
"session_key": session.export_session(messageIndex),
|
"session_key": session.export_session(messageIndex),
|
||||||
"forwarding_curve25519_key_chain":
|
"forwarding_curve25519_key_chain": session.forwardingCurve25519KeyChain || [],
|
||||||
session.forwardingCurve25519KeyChain || [],
|
|
||||||
};
|
};
|
||||||
} finally {
|
});
|
||||||
session.free();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|||||||
@@ -927,10 +927,18 @@ MegolmDecryption.prototype._buildKeyForwardingMessage = async function(
|
|||||||
* @param {module:crypto/OlmDevice.MegolmSessionData} session
|
* @param {module:crypto/OlmDevice.MegolmSessionData} session
|
||||||
*/
|
*/
|
||||||
MegolmDecryption.prototype.importRoomKey = function(session) {
|
MegolmDecryption.prototype.importRoomKey = function(session) {
|
||||||
this._olmDevice.importInboundGroupSession(session);
|
return this._olmDevice.addInboundGroupSession(
|
||||||
|
session.room_id,
|
||||||
// have another go at decrypting events sent with this session.
|
session.sender_key,
|
||||||
this._retryDecryption(session.sender_key, session.session_id);
|
session.forwarding_curve25519_key_chain,
|
||||||
|
session.session_id,
|
||||||
|
session.session_key,
|
||||||
|
session.sender_claimed_keys,
|
||||||
|
true,
|
||||||
|
).then(() => {
|
||||||
|
// have another go at decrypting events sent with this session.
|
||||||
|
this._retryDecryption(session.sender_key, session.session_id);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -700,21 +700,25 @@ Crypto.prototype.isRoomEncrypted = function(roomId) {
|
|||||||
/**
|
/**
|
||||||
* Get a list containing all of the room keys
|
* Get a list containing all of the room keys
|
||||||
*
|
*
|
||||||
* @return {module:client.Promise} a promise which resolves to a list of
|
* @return {module:crypto/OlmDevice.MegolmSessionData[]} a list of session export objects
|
||||||
* session export objects
|
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.exportRoomKeys = function() {
|
Crypto.prototype.exportRoomKeys = async function() {
|
||||||
return Promise.map(
|
const exportedSessions = [];
|
||||||
this._sessionStore.getAllEndToEndInboundGroupSessionKeys(),
|
await this._cryptoStore.doTxn(
|
||||||
(s) => {
|
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
||||||
return this._olmDevice.exportInboundGroupSession(
|
this._cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => {
|
||||||
s.senderKey, s.sessionId,
|
if (s === null) return;
|
||||||
).then((sess) => {
|
|
||||||
|
const sess = this._olmDevice.exportInboundGroupSession(
|
||||||
|
s.senderKey, s.sessionId, s.sessionData,
|
||||||
|
);
|
||||||
sess.algorithm = olmlib.MEGOLM_ALGORITHM;
|
sess.algorithm = olmlib.MEGOLM_ALGORITHM;
|
||||||
return sess;
|
exportedSessions.push(sess);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return exportedSessions;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import utils from '../../utils';
|
import utils from '../../utils';
|
||||||
|
|
||||||
export const VERSION = 3;
|
export const VERSION = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of a CryptoStore which is backed by an existing
|
* Implementation of a CryptoStore which is backed by an existing
|
||||||
@@ -258,6 +258,8 @@ export class Backend {
|
|||||||
return promiseifyTxn(txn);
|
return promiseifyTxn(txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm Account
|
||||||
|
|
||||||
getAccount(txn, func) {
|
getAccount(txn, func) {
|
||||||
const objectStore = txn.objectStore("account");
|
const objectStore = txn.objectStore("account");
|
||||||
const getReq = objectStore.get("-");
|
const getReq = objectStore.get("-");
|
||||||
@@ -275,6 +277,8 @@ export class Backend {
|
|||||||
objectStore.put(newData, "-");
|
objectStore.put(newData, "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm Sessions
|
||||||
|
|
||||||
countEndToEndSessions(txn, func) {
|
countEndToEndSessions(txn, func) {
|
||||||
const objectStore = txn.objectStore("sessions");
|
const objectStore = txn.objectStore("sessions");
|
||||||
const countReq = objectStore.count();
|
const countReq = objectStore.count();
|
||||||
@@ -324,6 +328,69 @@ export class Backend {
|
|||||||
objectStore.put({deviceKey, sessionId, session});
|
objectStore.put({deviceKey, sessionId, session});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inbound group sessions
|
||||||
|
|
||||||
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
|
const objectStore = txn.objectStore("inbound_group_sessions");
|
||||||
|
const getReq = objectStore.get([senderCurve25519Key, sessionId]);
|
||||||
|
getReq.onsuccess = function() {
|
||||||
|
try {
|
||||||
|
if (getReq.result) {
|
||||||
|
func(getReq.result.session);
|
||||||
|
} else {
|
||||||
|
func(null);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
abortWithException(txn, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllEndToEndInboundGroupSessions(txn, func) {
|
||||||
|
const objectStore = txn.objectStore("inbound_group_sessions");
|
||||||
|
const getReq = objectStore.openCursor();
|
||||||
|
getReq.onsuccess = function() {
|
||||||
|
const cursor = getReq.result;
|
||||||
|
if (cursor) {
|
||||||
|
try {
|
||||||
|
func({
|
||||||
|
senderKey: cursor.value.senderCurve25519Key,
|
||||||
|
sessionId: cursor.value.sessionId,
|
||||||
|
sessionData: cursor.value.session,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
abortWithException(txn, e);
|
||||||
|
}
|
||||||
|
cursor.continue();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
func(null);
|
||||||
|
} catch (e) {
|
||||||
|
abortWithException(txn, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
const objectStore = txn.objectStore("inbound_group_sessions");
|
||||||
|
const addReq = objectStore.add({
|
||||||
|
senderCurve25519Key, sessionId, session: sessionData,
|
||||||
|
});
|
||||||
|
addReq.onerror = () => {
|
||||||
|
abortWithException(txn, new Error(
|
||||||
|
"Failed to add inbound group session - session may already exist",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
const objectStore = txn.objectStore("inbound_group_sessions");
|
||||||
|
objectStore.put({
|
||||||
|
senderCurve25519Key, sessionId, session: sessionData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
doTxn(mode, stores, func) {
|
doTxn(mode, stores, func) {
|
||||||
const txn = this._db.transaction(stores, mode);
|
const txn = this._db.transaction(stores, mode);
|
||||||
const promise = promiseifyTxn(txn);
|
const promise = promiseifyTxn(txn);
|
||||||
@@ -351,6 +418,11 @@ export function upgradeDatabase(db, oldVersion) {
|
|||||||
});
|
});
|
||||||
sessionsStore.createIndex("deviceKey", "deviceKey");
|
sessionsStore.createIndex("deviceKey", "deviceKey");
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 4) {
|
||||||
|
db.createObjectStore("inbound_group_sessions", {
|
||||||
|
keyPath: ["senderCurve25519Key", "sessionId"],
|
||||||
|
});
|
||||||
|
}
|
||||||
// Expand as needed.
|
// Expand as needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,13 +448,28 @@ function abortWithException(txn, e) {
|
|||||||
// We could alternatively make the thing we pass back to the app
|
// We could alternatively make the thing we pass back to the app
|
||||||
// an object containing the transaction and exception.
|
// an object containing the transaction and exception.
|
||||||
txn._mx_abortexception = e;
|
txn._mx_abortexception = e;
|
||||||
txn.abort();
|
try {
|
||||||
|
txn.abort();
|
||||||
|
} catch (e) {
|
||||||
|
// sometimes we won't be able to abort the transaction
|
||||||
|
// (ie. if it's aborted or completed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function promiseifyTxn(txn) {
|
function promiseifyTxn(txn) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
txn.oncomplete = resolve;
|
txn.oncomplete = () => {
|
||||||
txn.onerror = reject;
|
if (txn._mx_abortexception !== undefined) {
|
||||||
|
reject(txn._mx_abortexception);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
txn.onerror = () => {
|
||||||
|
if (txn._mx_abortexception !== undefined) {
|
||||||
|
reject(txn._mx_abortexception);
|
||||||
|
}
|
||||||
|
reject();
|
||||||
|
};
|
||||||
txn.onabort = () => reject(txn._mx_abortexception);
|
txn.onabort = () => reject(txn._mx_abortexception);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,6 +227,8 @@ export default class IndexedDBCryptoStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm Account
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the account pickle from the store.
|
* Get the account pickle from the store.
|
||||||
* This requires an active transaction. See doTxn().
|
* This requires an active transaction. See doTxn().
|
||||||
@@ -249,6 +251,8 @@ export default class IndexedDBCryptoStore {
|
|||||||
this._backendPromise.value().storeAccount(txn, newData);
|
this._backendPromise.value().storeAccount(txn, newData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm sessions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of end-to-end sessions in the store
|
* Returns the number of end-to-end sessions in the store
|
||||||
* @param {*} txn An active transaction. See doTxn().
|
* @param {*} txn An active transaction. See doTxn().
|
||||||
@@ -296,6 +300,64 @@ export default class IndexedDBCryptoStore {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inbound group saessions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the end-to-end inbound group session for a given
|
||||||
|
* server key and session ID
|
||||||
|
* @param {string} senderCurve25519Key The sender's curve 25519 key
|
||||||
|
* @param {string} sessionId The ID of the session
|
||||||
|
* @param {*} txn An active transaction. See doTxn().
|
||||||
|
* @param {function(object)} func Called with A map from sessionId
|
||||||
|
* to Base64 end-to-end session.
|
||||||
|
*/
|
||||||
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
|
this._backendPromise.value().getEndToEndInboundGroupSession(
|
||||||
|
senderCurve25519Key, sessionId, txn, func,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all inbound group sessions in the store
|
||||||
|
* @param {*} txn An active transaction. See doTxn().
|
||||||
|
* @param {function(object)} func Called once for each group session
|
||||||
|
* in the store with an object having keys {senderKey, sessionId,
|
||||||
|
* sessionData}, then once with null to indicate the end of the list.
|
||||||
|
*/
|
||||||
|
getAllEndToEndInboundGroupSessions(txn, func) {
|
||||||
|
this._backendPromise.value().getAllEndToEndInboundGroupSessions(txn, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an end-to-end inbound group session to the store.
|
||||||
|
* If there already exists an inbound group session with the same
|
||||||
|
* senderCurve25519Key and sessionID, the session will not be added.
|
||||||
|
* @param {string} senderCurve25519Key The sender's curve 25519 key
|
||||||
|
* @param {string} sessionId The ID of the session
|
||||||
|
* @param {object} sessionData The session data structure
|
||||||
|
* @param {*} txn An active transaction. See doTxn().
|
||||||
|
*/
|
||||||
|
addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
this._backendPromise.value().addEndToEndInboundGroupSession(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an end-to-end inbound group session to the store.
|
||||||
|
* If there already exists an inbound group session with the same
|
||||||
|
* senderCurve25519Key and sessionID, it will be overwritten.
|
||||||
|
* @param {string} senderCurve25519Key The sender's curve 25519 key
|
||||||
|
* @param {string} sessionId The ID of the session
|
||||||
|
* @param {object} sessionData The session data structure
|
||||||
|
* @param {*} txn An active transaction. See doTxn().
|
||||||
|
*/
|
||||||
|
storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
this._backendPromise.value().storeEndToEndInboundGroupSession(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a transaction on the crypto store. Any store methods
|
* Perform a transaction on the crypto store. Any store methods
|
||||||
* that require a transaction (txn) object to be passed in may
|
* that require a transaction (txn) object to be passed in may
|
||||||
@@ -326,3 +388,4 @@ export default class IndexedDBCryptoStore {
|
|||||||
|
|
||||||
IndexedDBCryptoStore.STORE_ACCOUNT = 'account';
|
IndexedDBCryptoStore.STORE_ACCOUNT = 'account';
|
||||||
IndexedDBCryptoStore.STORE_SESSIONS = 'sessions';
|
IndexedDBCryptoStore.STORE_SESSIONS = 'sessions';
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS = 'inbound_group_sessions';
|
||||||
|
|||||||
@@ -29,11 +29,16 @@ import MemoryCryptoStore from './memory-crypto-store.js';
|
|||||||
|
|
||||||
const E2E_PREFIX = "crypto.";
|
const E2E_PREFIX = "crypto.";
|
||||||
const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
||||||
|
const KEY_INBOUND_SESSION_PREFIX = E2E_PREFIX + "inboundgroupsessions/";
|
||||||
|
|
||||||
function keyEndToEndSessions(deviceKey) {
|
function keyEndToEndSessions(deviceKey) {
|
||||||
return E2E_PREFIX + "sessions/" + deviceKey;
|
return E2E_PREFIX + "sessions/" + deviceKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keyEndToEndInboundGroupSession(senderKey, sessionId) {
|
||||||
|
return KEY_INBOUND_SESSION_PREFIX + senderKey + "/" + sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @implements {module:crypto/store/base~CryptoStore}
|
* @implements {module:crypto/store/base~CryptoStore}
|
||||||
*/
|
*/
|
||||||
@@ -43,6 +48,8 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore {
|
|||||||
this.store = global.localStorage;
|
this.store = global.localStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm Sessions
|
||||||
|
|
||||||
countEndToEndSessions(txn, func) {
|
countEndToEndSessions(txn, func) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (let i = 0; i < this.store.length; ++i) {
|
for (let i = 0; i < this.store.length; ++i) {
|
||||||
@@ -72,6 +79,54 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inbound Group Sessions
|
||||||
|
|
||||||
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
|
func(getJsonItem(
|
||||||
|
this.store,
|
||||||
|
keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllEndToEndInboundGroupSessions(txn, func) {
|
||||||
|
for (let i = 0; i < this.store.length; ++i) {
|
||||||
|
const key = this.store.key(i);
|
||||||
|
if (key.startsWith(KEY_INBOUND_SESSION_PREFIX)) {
|
||||||
|
// we can't use split, as the components we are trying to split out
|
||||||
|
// might themselves contain '/' characters. We rely on the
|
||||||
|
// senderKey being a (32-byte) curve25519 key, base64-encoded
|
||||||
|
// (hence 43 characters long).
|
||||||
|
|
||||||
|
func({
|
||||||
|
senderKey: key.substr(KEY_INBOUND_SESSION_PREFIX.length, 43),
|
||||||
|
sessionId: key.substr(KEY_INBOUND_SESSION_PREFIX.length + 44),
|
||||||
|
sessionData: getJsonItem(this.store, key),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
const existing = getJsonItem(
|
||||||
|
this.store,
|
||||||
|
keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId),
|
||||||
|
);
|
||||||
|
if (!existing) {
|
||||||
|
this.storeEndToEndInboundGroupSession(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
setJsonItem(
|
||||||
|
this.store,
|
||||||
|
keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId),
|
||||||
|
sessionData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all data from this store.
|
* Delete all data from this store.
|
||||||
*
|
*
|
||||||
@@ -82,6 +137,8 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm account
|
||||||
|
|
||||||
getAccount(txn, func) {
|
getAccount(txn, func) {
|
||||||
const account = this.store.getItem(KEY_END_TO_END_ACCOUNT);
|
const account = this.store.getItem(KEY_END_TO_END_ACCOUNT);
|
||||||
func(account);
|
func(account);
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export default class MemoryCryptoStore {
|
|||||||
|
|
||||||
// Map of {devicekey -> {sessionId -> session pickle}}
|
// Map of {devicekey -> {sessionId -> session pickle}}
|
||||||
this._sessions = {};
|
this._sessions = {};
|
||||||
|
// Map of {senderCurve25519Key+'/'+sessionId -> session data object}
|
||||||
|
this._inboundGroupSessions = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,6 +203,8 @@ export default class MemoryCryptoStore {
|
|||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm Account
|
||||||
|
|
||||||
getAccount(txn, func) {
|
getAccount(txn, func) {
|
||||||
func(this._account);
|
func(this._account);
|
||||||
}
|
}
|
||||||
@@ -209,6 +213,8 @@ export default class MemoryCryptoStore {
|
|||||||
this._account = newData;
|
this._account = newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Olm Sessions
|
||||||
|
|
||||||
countEndToEndSessions(txn, func) {
|
countEndToEndSessions(txn, func) {
|
||||||
return Object.keys(this._sessions).length;
|
return Object.keys(this._sessions).length;
|
||||||
}
|
}
|
||||||
@@ -231,6 +237,40 @@ export default class MemoryCryptoStore {
|
|||||||
deviceSessions[sessionId] = session;
|
deviceSessions[sessionId] = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inbound Group Sessions
|
||||||
|
|
||||||
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
|
func(this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] || null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllEndToEndInboundGroupSessions(txn, func) {
|
||||||
|
for (const key of Object.keys(this._inboundGroupSessions)) {
|
||||||
|
// we can't use split, as the components we are trying to split out
|
||||||
|
// might themselves contain '/' characters. We rely on the
|
||||||
|
// senderKey being a (32-byte) curve25519 key, base64-encoded
|
||||||
|
// (hence 43 characters long).
|
||||||
|
|
||||||
|
func({
|
||||||
|
senderKey: key.substr(0, 43),
|
||||||
|
sessionId: key.substr(44),
|
||||||
|
sessionData: this._inboundGroupSessions[key],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
func(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
const k = senderCurve25519Key+'/'+sessionId;
|
||||||
|
if (this._inboundGroupSessions[k] === undefined) {
|
||||||
|
this._inboundGroupSessions[k] = sessionData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
|
||||||
|
this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] = sessionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
doTxn(mode, stores, func) {
|
doTxn(mode, stores, func) {
|
||||||
return Promise.resolve(func(null));
|
return Promise.resolve(func(null));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,9 +178,8 @@ WebStorageSessionStore.prototype = {
|
|||||||
return this.store.getItem(key);
|
return this.store.getItem(key);
|
||||||
},
|
},
|
||||||
|
|
||||||
storeEndToEndInboundGroupSession: function(senderKey, sessionId, pickledSession) {
|
removeAllEndToEndInboundGroupSessions: function() {
|
||||||
const key = keyEndToEndInboundGroupSession(senderKey, sessionId);
|
removeByPrefix(this.store, E2E_PREFIX + 'inboundgroupsessions/');
|
||||||
return this.store.setItem(key, pickledSession);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user