Search in sources :

Example 6 with MXDeviceInfo

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);
        }
    }
}
Also used : RoomKeyRequestBody(org.matrix.androidsdk.rest.model.crypto.RoomKeyRequestBody) MXDeviceInfo(org.matrix.androidsdk.crypto.data.MXDeviceInfo) ArrayList(java.util.ArrayList) IMXDecrypting(org.matrix.androidsdk.crypto.algorithms.IMXDecrypting)

Example 7 with MXDeviceInfo

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;
}
Also used : MXDeviceInfo(org.matrix.androidsdk.crypto.data.MXDeviceInfo) MXUsersDevicesMap(org.matrix.androidsdk.crypto.data.MXUsersDevicesMap)

Example 8 with MXDeviceInfo

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);
            }
        }
    });
}
Also used : IMXCryptoStore(org.matrix.androidsdk.data.cryptostore.IMXCryptoStore) HashMap(java.util.HashMap) MXDeviceInfo(org.matrix.androidsdk.crypto.data.MXDeviceInfo) ArrayList(java.util.ArrayList) KeysQueryResponse(org.matrix.androidsdk.rest.model.crypto.KeysQueryResponse) MatrixError(org.matrix.androidsdk.rest.model.MatrixError)

Example 9 with MXDeviceInfo

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);
        }
    });
}
Also used : MXDeviceInfo(org.matrix.androidsdk.crypto.data.MXDeviceInfo) MXUsersDevicesMap(org.matrix.androidsdk.crypto.data.MXUsersDevicesMap) MatrixError(org.matrix.androidsdk.rest.model.MatrixError) MXCryptoError(org.matrix.androidsdk.crypto.MXCryptoError)

Example 10 with MXDeviceInfo

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;
        }
    });
}
Also used : HashMap(java.util.HashMap) MXDeviceInfo(org.matrix.androidsdk.crypto.data.MXDeviceInfo) ArrayList(java.util.ArrayList) MatrixError(org.matrix.androidsdk.rest.model.MatrixError)

Aggregations

MXDeviceInfo (org.matrix.androidsdk.crypto.data.MXDeviceInfo)25 MatrixError (org.matrix.androidsdk.rest.model.MatrixError)17 HashMap (java.util.HashMap)15 MXUsersDevicesMap (org.matrix.androidsdk.crypto.data.MXUsersDevicesMap)13 ArrayList (java.util.ArrayList)12 CountDownLatch (java.util.concurrent.CountDownLatch)10 JsonObject (com.google.gson.JsonObject)8 Test (org.junit.Test)8 Context (android.content.Context)7 Room (org.matrix.androidsdk.data.Room)6 MXEventListener (org.matrix.androidsdk.listeners.MXEventListener)6 MXCryptoError (org.matrix.androidsdk.crypto.MXCryptoError)4 MXOlmSessionResult (org.matrix.androidsdk.crypto.data.MXOlmSessionResult)4 Uri (android.net.Uri)3 List (java.util.List)3 RoomState (org.matrix.androidsdk.data.RoomState)3 IMXStore (org.matrix.androidsdk.data.store.IMXStore)3 MXFileStore (org.matrix.androidsdk.data.store.MXFileStore)3 MXStoreListener (org.matrix.androidsdk.data.store.MXStoreListener)3 ApiCallback (org.matrix.androidsdk.rest.callback.ApiCallback)3