use of org.matrix.androidsdk.crypto.data.MXDeviceInfo in project matrix-android-sdk by matrix-org.
the class MXCrypto method processReceivedRoomKeyRequests.
/**
* Process any m.room_key_request events which were queued up during the
* current sync.
*/
private void processReceivedRoomKeyRequests() {
List<IncomingRoomKeyRequest> receivedRoomKeyRequests = null;
synchronized (mReceivedRoomKeyRequests) {
if (!mReceivedRoomKeyRequests.isEmpty()) {
receivedRoomKeyRequests = new ArrayList(mReceivedRoomKeyRequests);
mReceivedRoomKeyRequests.clear();
}
}
if (null != receivedRoomKeyRequests) {
for (final IncomingRoomKeyRequest request : receivedRoomKeyRequests) {
String userId = request.mUserId;
String deviceId = request.mDeviceId;
RoomKeyRequestBody body = request.mRequestBody;
String roomId = body.room_id;
String alg = body.algorithm;
Log.d(LOG_TAG, "m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.session_id + " id " + request.mRequestId);
if (!TextUtils.equals(mSession.getMyUserId(), userId)) {
// TODO: determine if we sent this device the keys already: in
Log.e(LOG_TAG, "## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now");
return;
}
// todo: should we queue up requests we don't yet have keys for,
// in case they turn up later?
// if we don't have a decryptor for this room/alg, we don't have
// the keys for the requested events, and can drop the requests.
final IMXDecrypting decryptor = getRoomDecryptor(roomId, alg);
if (null == decryptor) {
Log.e(LOG_TAG, "## processReceivedRoomKeyRequests() : room key request for unknown " + alg + " in room " + roomId);
continue;
}
if (!decryptor.hasKeysForKeyRequest(request)) {
Log.e(LOG_TAG, "## processReceivedRoomKeyRequests() : room key request for unknown session " + body.session_id);
mCryptoStore.deleteIncomingRoomKeyRequest(request);
continue;
}
if (TextUtils.equals(deviceId, getMyDevice().deviceId) && TextUtils.equals(mSession.getMyUserId(), userId)) {
Log.d(LOG_TAG, "## processReceivedRoomKeyRequests() : oneself device - ignored");
mCryptoStore.deleteIncomingRoomKeyRequest(request);
continue;
}
request.mShare = new Runnable() {
@Override
public void run() {
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
decryptor.shareKeysWithDevice(request);
mCryptoStore.deleteIncomingRoomKeyRequest(request);
}
});
}
};
// if the device is is verified already, share the keys
MXDeviceInfo device = mCryptoStore.getUserDevice(deviceId, userId);
if (null != device) {
if (device.isVerified()) {
Log.d(LOG_TAG, "## processReceivedRoomKeyRequests() : device is already verified: sharing keys");
mCryptoStore.deleteIncomingRoomKeyRequest(request);
request.mShare.run();
continue;
}
if (device.isBlocked()) {
Log.d(LOG_TAG, "## processReceivedRoomKeyRequests() : device is blocked -> ignored");
mCryptoStore.deleteIncomingRoomKeyRequest(request);
continue;
}
}
mCryptoStore.storeIncomingRoomKeyRequest(request);
onRoomKeyRequest(request);
}
}
List<IncomingRoomKeyRequestCancellation> receivedRoomKeyRequestCancellations = null;
synchronized (mReceivedRoomKeyRequestCancellations) {
if (!mReceivedRoomKeyRequestCancellations.isEmpty()) {
receivedRoomKeyRequestCancellations = new ArrayList(mReceivedRoomKeyRequestCancellations);
mReceivedRoomKeyRequestCancellations.clear();
}
}
if (null != receivedRoomKeyRequestCancellations) {
for (IncomingRoomKeyRequestCancellation request : receivedRoomKeyRequestCancellations) {
Log.d(LOG_TAG, "## ## processReceivedRoomKeyRequests() : m.room_key_request cancellation for " + request.mUserId + ":" + request.mDeviceId + " id " + request.mRequestId);
// we should probably only notify the app of cancellations we told it
// about, but we don't currently have a record of that, so we just pass
// everything through.
onRoomKeyRequestCancellation(request);
mCryptoStore.deleteIncomingRoomKeyRequest(request);
}
}
}
use of org.matrix.androidsdk.crypto.data.MXDeviceInfo in project matrix-android-sdk by matrix-org.
the class MXCrypto method getUnknownDevices.
/**
* Provides the list of unknown devices
*
* @param devicesInRoom the devices map
* @return the unknown devices map
*/
public static MXUsersDevicesMap<MXDeviceInfo> getUnknownDevices(MXUsersDevicesMap<MXDeviceInfo> devicesInRoom) {
MXUsersDevicesMap<MXDeviceInfo> unknownDevices = new MXUsersDevicesMap<>();
List<String> userIds = devicesInRoom.getUserIds();
for (String userId : userIds) {
List<String> deviceIds = devicesInRoom.getUserDeviceIds(userId);
for (String deviceId : deviceIds) {
MXDeviceInfo deviceInfo = devicesInRoom.getObject(deviceId, userId);
if (deviceInfo.isUnknown()) {
unknownDevices.setObject(deviceInfo, userId, deviceId);
}
}
}
return unknownDevices;
}
use of org.matrix.androidsdk.crypto.data.MXDeviceInfo in project matrix-android-sdk by matrix-org.
the class MXDeviceList method doKeyDownloadForUsers.
/**
* Download the devices keys for a set of users.
* It must be called in getEncryptingThreadHandler() thread.
* The callback is called in the UI thread.
*
* @param downloadUsers the user ids list
* @param callback the asynchronous callback
*/
private void doKeyDownloadForUsers(final List<String> downloadUsers, final ApiCallback<MXUsersDevicesMap<MXDeviceInfo>> callback) {
Log.d(LOG_TAG, "## doKeyDownloadForUsers() : doKeyDownloadForUsers " + downloadUsers);
// get the user ids which did not already trigger a keys download
final List<String> filteredUsers = addDownloadKeysPromise(downloadUsers, callback);
// if there is no new keys request
if (0 == filteredUsers.size()) {
// trigger nothing
return;
}
// sanity check
if ((null == mxSession.getDataHandler()) || (null == mxSession.getDataHandler().getStore())) {
return;
}
mIsDownloadingKeys = true;
// track the race condition while sending requests
// we defines a tag for each request
// and test if the response is the latest request one
final String downloadToken = filteredUsers.hashCode() + " " + System.currentTimeMillis();
for (String userId : filteredUsers) {
mPendingDownloadKeysRequestToken.put(userId, downloadToken);
}
mxSession.getCryptoRestClient().downloadKeysForUsers(filteredUsers, mxSession.getDataHandler().getStore().getEventStreamToken(), new ApiCallback<KeysQueryResponse>() {
@Override
public void onSuccess(final KeysQueryResponse keysQueryResponse) {
mxCrypto.getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG, "## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size() + " users");
MXDeviceInfo myDevice = mxCrypto.getMyDevice();
IMXCryptoStore cryptoStore = mxCrypto.getCryptoStore();
List<String> userIdsList = new ArrayList<>(filteredUsers);
for (String userId : userIdsList) {
// test if the response is the latest request one
if (!TextUtils.equals(mPendingDownloadKeysRequestToken.get(userId), downloadToken)) {
Log.e(LOG_TAG, "## doKeyDownloadForUsers() : Another update in the queue for " + userId + " not marking up-to-date");
filteredUsers.remove(userId);
} else {
Map<String, MXDeviceInfo> devices = keysQueryResponse.deviceKeys.get(userId);
Log.d(LOG_TAG, "## doKeyDownloadForUsers() : Got keys for " + userId + " : " + devices);
if (null != devices) {
HashMap<String, MXDeviceInfo> mutableDevices = new HashMap<>(devices);
ArrayList<String> deviceIds = new ArrayList<>(mutableDevices.keySet());
for (String deviceId : deviceIds) {
// the user has been logged out
if (null == cryptoStore) {
break;
}
// Get the potential previously store device keys for this device
MXDeviceInfo previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId);
MXDeviceInfo deviceInfo = mutableDevices.get(deviceId);
// the self device must be seen as verified
if (TextUtils.equals(deviceInfo.deviceId, myDevice.deviceId) && TextUtils.equals(userId, myDevice.userId)) {
deviceInfo.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED;
}
// Validate received keys
if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) {
// New device keys are not valid. Do not store them
mutableDevices.remove(deviceId);
if (null != previouslyStoredDeviceKeys) {
// But keep old validated ones if any
mutableDevices.put(deviceId, previouslyStoredDeviceKeys);
}
} else if (null != previouslyStoredDeviceKeys) {
// The verified status is not sync'ed with hs.
// This is a client side information, valid only for this client.
// So, transfer its previous value
mutableDevices.get(deviceId).mVerified = previouslyStoredDeviceKeys.mVerified;
}
}
// Update the store
// Note that devices which aren't in the response will be removed from the stores
cryptoStore.storeUserDevices(userId, mutableDevices);
}
// the response is the latest request one
mPendingDownloadKeysRequestToken.remove(userId);
}
}
onKeysDownloadSucceed(filteredUsers, keysQueryResponse.failures);
}
});
}
private void onFailed() {
mxCrypto.getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
List<String> userIdsList = new ArrayList<>(filteredUsers);
// test if the response is the latest request one
for (String userId : userIdsList) {
if (!TextUtils.equals(mPendingDownloadKeysRequestToken.get(userId), downloadToken)) {
Log.e(LOG_TAG, "## doKeyDownloadForUsers() : Another update in the queue for " + userId + " not marking up-to-date");
filteredUsers.remove(userId);
} else {
// the response is the latest request one
mPendingDownloadKeysRequestToken.remove(userId);
}
}
onKeysDownloadFailed(filteredUsers);
}
});
}
@Override
public void onNetworkError(Exception e) {
Log.e(LOG_TAG, "##doKeyDownloadForUsers() : onNetworkError " + e.getMessage());
onFailed();
if (null != callback) {
callback.onNetworkError(e);
}
}
@Override
public void onMatrixError(MatrixError e) {
Log.e(LOG_TAG, "##doKeyDownloadForUsers() : onMatrixError " + e.getMessage());
onFailed();
if (null != callback) {
callback.onMatrixError(e);
}
}
@Override
public void onUnexpectedError(Exception e) {
Log.e(LOG_TAG, "##doKeyDownloadForUsers() : onUnexpectedError " + e.getMessage());
onFailed();
if (null != callback) {
callback.onUnexpectedError(e);
}
}
});
}
use of org.matrix.androidsdk.crypto.data.MXDeviceInfo in project matrix-android-sdk by matrix-org.
the class MXMegolmEncryption method getDevicesInRoom.
/**
* Get the list of devices which can encrypt data to.
* This method must be called in getDecryptingThreadHandler() thread.
*
* @param userIds the user ids whose devices must be checked.
* @param callback the asynchronous callback
*/
private void getDevicesInRoom(final List<String> userIds, final ApiCallback<MXUsersDevicesMap<MXDeviceInfo>> callback) {
// We are happy to use a cached version here: we assume that if we already
// have a list of the user's devices, then we already share an e2e room
// with them, which means that they will have announced any new devices via
// an m.new_device.
mCrypto.getDeviceList().downloadKeys(userIds, false, new ApiCallback<MXUsersDevicesMap<MXDeviceInfo>>() {
@Override
public void onSuccess(final MXUsersDevicesMap<MXDeviceInfo> devices) {
mCrypto.getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
boolean encryptToVerifiedDevicesOnly = mCrypto.getGlobalBlacklistUnverifiedDevices() || mCrypto.isRoomBlacklistUnverifiedDevices(mRoomId);
final MXUsersDevicesMap<MXDeviceInfo> devicesInRoom = new MXUsersDevicesMap<>();
final MXUsersDevicesMap<MXDeviceInfo> unknownDevices = new MXUsersDevicesMap<>();
List<String> userIds = devices.getUserIds();
for (String userId : userIds) {
List<String> deviceIds = devices.getUserDeviceIds(userId);
for (String deviceId : deviceIds) {
MXDeviceInfo deviceInfo = devices.getObject(deviceId, userId);
if (mCrypto.warnOnUnknownDevices() && deviceInfo.isUnknown()) {
// The device is not yet known by the user
unknownDevices.setObject(deviceInfo, userId, deviceId);
continue;
}
if (deviceInfo.isBlocked()) {
// Remove any blocked devices
continue;
}
if (!deviceInfo.isVerified() && encryptToVerifiedDevicesOnly) {
continue;
}
if (TextUtils.equals(deviceInfo.identityKey(), mCrypto.getOlmDevice().getDeviceCurve25519Key())) {
// Don't bother sending to ourself
continue;
}
devicesInRoom.setObject(deviceInfo, userId, deviceId);
}
}
mCrypto.getUIHandler().post(new Runnable() {
@Override
public void run() {
// if so, warn the user so they can verify or ignore.
if (0 != unknownDevices.getMap().size()) {
callback.onMatrixError(new MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices));
} else {
callback.onSuccess(devicesInRoom);
}
}
});
}
});
}
@Override
public void onNetworkError(Exception e) {
callback.onNetworkError(e);
}
@Override
public void onMatrixError(MatrixError e) {
callback.onMatrixError(e);
}
@Override
public void onUnexpectedError(Exception e) {
callback.onUnexpectedError(e);
}
});
}
use of org.matrix.androidsdk.crypto.data.MXDeviceInfo in project matrix-android-sdk by matrix-org.
the class MXMegolmEncryption method ensureOutboundSession.
/**
* Ensure the outbound session
*
* @param devicesInRoom the devices list
* @param callback the asynchronous callback.
*/
private void ensureOutboundSession(MXUsersDevicesMap<MXDeviceInfo> devicesInRoom, final ApiCallback<MXOutboundSessionInfo> callback) {
MXOutboundSessionInfo session = mOutboundSession;
if ((null == session) || // Need to make a brand new session?
session.needsRotation(mSessionRotationPeriodMsgs, mSessionRotationPeriodMs) || // Determine if we have shared with anyone we shouldn't have
session.sharedWithTooManyDevices(devicesInRoom)) {
mOutboundSession = session = prepareNewSessionInRoom();
}
if (mShareOperationIsProgress) {
Log.d(LOG_TAG, "## ensureOutboundSessionInRoom() : already in progress");
// Key share already in progress
return;
}
final MXOutboundSessionInfo fSession = session;
HashMap<String, ArrayList<MXDeviceInfo>> /* userId */
shareMap = new HashMap<>();
List<String> userIds = devicesInRoom.getUserIds();
for (String userId : userIds) {
List<String> deviceIds = devicesInRoom.getUserDeviceIds(userId);
for (String deviceId : deviceIds) {
MXDeviceInfo deviceInfo = devicesInRoom.getObject(deviceId, userId);
if (null == fSession.mSharedWithDevices.getObject(deviceId, userId)) {
if (!shareMap.containsKey(userId)) {
shareMap.put(userId, new ArrayList<MXDeviceInfo>());
}
shareMap.get(userId).add(deviceInfo);
}
}
}
shareKey(fSession, shareMap, new ApiCallback<Void>() {
@Override
public void onSuccess(Void anything) {
mShareOperationIsProgress = false;
if (null != callback) {
callback.onSuccess(fSession);
}
}
@Override
public void onNetworkError(final Exception e) {
Log.e(LOG_TAG, "## ensureOutboundSessionInRoom() : shareKey onNetworkError " + e.getMessage());
if (null != callback) {
callback.onNetworkError(e);
}
mShareOperationIsProgress = false;
}
@Override
public void onMatrixError(final MatrixError e) {
Log.e(LOG_TAG, "## ensureOutboundSessionInRoom() : shareKey onMatrixError " + e.getMessage());
if (null != callback) {
callback.onMatrixError(e);
}
mShareOperationIsProgress = false;
}
@Override
public void onUnexpectedError(final Exception e) {
Log.e(LOG_TAG, "## ensureOutboundSessionInRoom() : shareKey onUnexpectedError " + e.getMessage());
if (null != callback) {
callback.onUnexpectedError(e);
}
mShareOperationIsProgress = false;
}
});
}
Aggregations