Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

Add blacklisting unverified devices #139

Merged
merged 6 commits into from
Mar 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
529 changes: 522 additions & 7 deletions matrix-sdk/src/androidTest/java/org/matrix/androidsdk/CryptoTest.java

Large diffs are not rendered by default.

208 changes: 208 additions & 0 deletions matrix-sdk/src/main/java/org/matrix/androidsdk/crypto/MXCrypto.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import android.text.TextUtils;

import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2;
import org.matrix.androidsdk.rest.callback.SimpleApiCallback;
import org.matrix.androidsdk.util.Log;

import com.google.gson.JsonElement;
Expand Down Expand Up @@ -2322,4 +2323,211 @@ public void onUnexpectedError(Exception e) {
}
});
}

/**
* Set the global override for whether the client should ever send encrypted
* messages to unverified devices.
* If false, it can still be overridden per-room.
* If true, it overrides the per-room settings.
* @param block true to unilaterally blacklist all
* @param callback the asynchronous callback.
*/
public void setGlobalBlacklistUnverifiedDevices(final boolean block, final ApiCallback<Void> callback) {
final String userId = mSession.getMyUserId();
final ArrayList<String> userRoomIds = new ArrayList<>();

Collection<Room> rooms = mSession.getDataHandler().getStore().getRooms();

for (Room room : rooms) {
if (room.isEncrypted()) {
RoomMember roomMember = room.getMember(userId);

// test if the user joins the room
if ((null != roomMember) && TextUtils.equals(roomMember.membership, RoomMember.MEMBERSHIP_JOIN)) {
userRoomIds.add(room.getRoomId());
}
}
}

getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
mCryptoStore.setGlobalBlacklistUnverifiedDevices(block);
for (String roomId : userRoomIds) {
IMXEncrypting alg;

synchronized (mRoomEncryptors) {
alg = mRoomEncryptors.get(roomId);
}

if (null != alg) {
alg.onBlacklistUnverifiedDevices();
}
}

getUIHandler().post(new Runnable() {
@Override
public void run() {
if (null != callback) {
callback.onSuccess(null);
}
}
});
}
});
}

/**
* Tells whether the client should ever send encrypted messages to unverified devices.
* The default value is false.
* This function must be called in the getEncryptingThreadHandler() thread.
* @return true to unilaterally blacklist all unverified devices.
*/
public boolean getGlobalBlacklistUnverifiedDevices() {
return mCryptoStore.getGlobalBlacklistUnverifiedDevices();
}

/**
* Tells whether the client should ever send encrypted messages to unverified devices.
* The default value is false.
* messages to unverified devices.
* @param callback the asynchronous callback
*/
public void getGlobalBlacklistUnverifiedDevices(final ApiCallback<Boolean> callback) {
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
if (null != callback) {
final boolean status = getGlobalBlacklistUnverifiedDevices();

getUIHandler().post(new Runnable() {
@Override
public void run() {
callback.onSuccess(status);
}
});
}
}
});
}

/**
* Tells whether the client should encrypt messages only for the verified devices
* in this room.
* The default value is false.
* This function must be called in the getEncryptingThreadHandler() thread.
* @param roomId the room id
* @return true if the client should encrypt messages only for the verified devices.
*/
public boolean isRoomBlacklistUnverifiedDevices(String roomId) {
if (null != roomId) {
return mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId);
} else {
return false;
}
}

/**
* Tells whether the client should encrypt messages only for the verified devices
* in this room.
* The default value is false.
* This function must be called in the getEncryptingThreadHandler() thread.
* @param roomId the room id
* @param callback the asynchronous callback
*/
public void isRoomBlacklistUnverifiedDevices(final String roomId, final ApiCallback<Boolean> callback) {
getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
final boolean status = isRoomBlacklistUnverifiedDevices(roomId);

getUIHandler().post(new Runnable() {
@Override
public void run() {
if (null != callback) {
callback.onSuccess(status);
}
}
});
}
});
}

/**
* Manages the room black-listing for unverified devices.
* @param roomId the room id
* @param add true to add the room id to the list, false to remove it.
* @param callback the asynchronous callback
*/
private void setRoomBlacklistUnverifiedDevices(final String roomId, final boolean add, final ApiCallback<Void> callback) {
final Room room = mSession.getDataHandler().getRoom(roomId);

// sanity check
if (null == room) {
getUIHandler().post(new Runnable() {
@Override
public void run() {
callback.onSuccess(null);
}
});

return;
}

getEncryptingThreadHandler().post(new Runnable() {
@Override
public void run() {
List<String> roomIds = mCryptoStore.getRoomsListBlacklistUnverifiedDevices();

if (add) {
if (!roomIds.contains(roomId)) {
roomIds.add(roomId);
}
} else {
roomIds.remove(roomId);
}

mCryptoStore.setRoomsListBlacklistUnverifiedDevices(roomIds);

// warn the dedicated
IMXEncrypting alg;

synchronized (mRoomEncryptors) {
alg = mRoomEncryptors.get(roomId);
}

if (null != alg) {
alg.onBlacklistUnverifiedDevices();
}

getUIHandler().post(new Runnable() {
@Override
public void run() {
if (null != callback) {
callback.onSuccess(null);
}
}
});
}
});
}


/**
* Add this room to the ones which don't encrypt messages to unverified devices.
* @param roomId the room id
* @param callback the asynchronous callback
*/
public void setRoomBlacklistUnverifiedDevices(final String roomId, final ApiCallback<Void> callback) {
setRoomBlacklistUnverifiedDevices(roomId, true, callback);
}

/**
* Remove this room to the ones which don't encrypt messages to unverified devices.
* @param roomId the room id
* @param callback the asynchronous callback
*/
public void setRoomUnblacklistUnverifiedDevices(final String roomId, final ApiCallback<Void> callback) {
setRoomBlacklistUnverifiedDevices(roomId, false, callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ public interface IMXEncrypting {
* @param oldVerified the old verification status.
*/
void onDeviceVerification(MXDeviceInfo device, int oldVerified);

/**
* Called when the unverified devices list status has been toggled.
*/
void onBlacklistUnverifiedDevices();
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ public void onDeviceVerification(MXDeviceInfo device, int oldVerified) {
// message thanks to [self ensureOutboundSessionInRoom]
}

@Override
public void onBlacklistUnverifiedDevices() {
mOutboundSession = null;
}

/**
* Prepare a new session.
*
Expand Down Expand Up @@ -248,6 +253,7 @@ public void onSuccess(final MXUsersDevicesMap<MXDeviceInfo> usersDevices) {
@Override
public void run() {
Log.d(LOG_TAG, "## ensureOutboundSessionInRoom() : getDevicesInRoom() succeeds after " + (System.currentTimeMillis() - t0) + " ms");
boolean encryptToVerifiedDevicesOnly = mCrypto.getGlobalBlacklistUnverifiedDevices() || mCrypto.isRoomBlacklistUnverifiedDevices(mRoomId);

if (mCrypto.warnOnUnknownDevices()) {
final MXUsersDevicesMap<MXDeviceInfo> unknownDevices = MXCrypto.getUnknownDevices(usersDevices);
Expand Down Expand Up @@ -280,6 +286,10 @@ public void run() {
continue;
}

if (!deviceInfo.isVerified() && encryptToVerifiedDevicesOnly) {
continue;
}

if (TextUtils.equals(deviceInfo.identityKey(), mCrypto.getOlmDevice().getDeviceCurve25519Key())) {
// Don't bother sending to ourself
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ public void onRoomMembership(Event event, RoomMember member, String oldMembershi

@Override
public void onDeviceVerification(MXDeviceInfo device, int oldVerified) {
// No impact for olm
}

@Override
public void onBlacklistUnverifiedDevices() {
// No impact for olm
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,30 @@ public interface IMXCryptoStore {
* @param senderKey the base64-encoded curve25519 key of the sender.
*/
void removeInboundGroupSession(String sessionId, String senderKey);

/**
* Set the global override for whether the client should ever send encrypted
* messages to unverified devices.
* If false, it can still be overridden per-room.
* If true, it overrides the per-room settings.
* @param block true to unilaterally blacklist all
*/
void setGlobalBlacklistUnverifiedDevices(boolean block);

/**
* @return true to unilaterally blacklist all unverified devices.
*/
boolean getGlobalBlacklistUnverifiedDevices();

/**
* Updates the rooms ids list in which the messages are not encrypted for the unverified devices.
* @param roomIds the room ids list
*/
void setRoomsListBlacklistUnverifiedDevices(List<String> roomIds);

/**
* Provides the rooms ids list in which the messages are not encrypted for the unverified devices.
* @return the room Ids list
*/
List<String> getRoomsListBlacklistUnverifiedDevices();
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public class MXFileCryptoStore implements IMXCryptoStore {
private Credentials mCredentials;

// Meta data about the store
private MXFileCryptoStoreMetaData mMetaData;
private MXFileCryptoStoreMetaData2 mMetaData;

// The olm account
private OlmAccount mOlmAccount;
Expand Down Expand Up @@ -168,13 +168,7 @@ public void initWithCredentials(Context context, Credentials credentials) {
&& (null != credentials.homeServer)
&& (null != credentials.userId)
&& (null != credentials.accessToken)) {
mMetaData = new MXFileCryptoStoreMetaData();
mMetaData.mUserId = new String(mCredentials.userId);
if (null != mCredentials.deviceId) {
mMetaData.mDeviceId = new String(mCredentials.deviceId);
}
mMetaData.mVersion = MXFILE_CRYPTO_VERSION;
mMetaData.mDeviceAnnounced = false;
mMetaData = new MXFileCryptoStoreMetaData2(mCredentials.userId, mCredentials.deviceId, MXFILE_CRYPTO_VERSION);
}

mUsersDevicesInfoMap = new MXUsersDevicesMap<>();
Expand Down Expand Up @@ -251,13 +245,7 @@ else if (!TextUtils.equals(mMetaData.mUserId, mCredentials.userId) ||
&& (null != mCredentials.homeServer)
&& (null != mCredentials.userId)
&& (null != mCredentials.accessToken)) {
mMetaData = new MXFileCryptoStoreMetaData();
mMetaData.mUserId = new String(mCredentials.userId);
if (null != mCredentials.deviceId) {
mMetaData.mDeviceId = new String(mCredentials.deviceId);
}
mMetaData.mVersion = MXFILE_CRYPTO_VERSION;
mMetaData.mDeviceAnnounced = false;
mMetaData = new MXFileCryptoStoreMetaData2(mCredentials.userId, mCredentials.deviceId, MXFILE_CRYPTO_VERSION);
saveMetaData();
}
}
Expand Down Expand Up @@ -644,6 +632,32 @@ public void close() {
mInboundGroupSessions.clear();
}

@Override
public void setGlobalBlacklistUnverifiedDevices(boolean block) {
mMetaData.mGlobalBlacklistUnverifiedDevices = block;
saveMetaData();
}

@Override
public boolean getGlobalBlacklistUnverifiedDevices() {
return mMetaData.mGlobalBlacklistUnverifiedDevices;
}

@Override
public void setRoomsListBlacklistUnverifiedDevices(List<String> roomIds) {
mMetaData.mBlacklistUnverifiedDevicesRoomIdsList = roomIds;
saveMetaData();
}

@Override
public List<String> getRoomsListBlacklistUnverifiedDevices() {
if (null == mMetaData.mBlacklistUnverifiedDevicesRoomIdsList) {
return new ArrayList<>();
} else {
return new ArrayList<>(mMetaData.mBlacklistUnverifiedDevicesRoomIdsList);
}
}

/**
* Reset the crypto store data
*/
Expand Down Expand Up @@ -726,7 +740,11 @@ private void loadMetaData() {

if (null != metadataAsVoid) {
try {
mMetaData = (MXFileCryptoStoreMetaData) metadataAsVoid;
if (metadataAsVoid instanceof MXFileCryptoStoreMetaData2) {
mMetaData = (MXFileCryptoStoreMetaData2) metadataAsVoid;
} else {
mMetaData = new MXFileCryptoStoreMetaData2((MXFileCryptoStoreMetaData)metadataAsVoid);
}
} catch (Exception e) {
mIsCorrupted = true;
Log.e(LOG_TAG, "## loadMetadata() : metadata has been corrupted");
Expand Down
Loading