/*
* Copyright 2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.androidsdk.crypto;
import android.text.TextUtils;
import org.matrix.androidsdk.util.Log;
import com.google.gson.JsonParser;
import org.matrix.androidsdk.crypto.algorithms.MXDecryptionResult;
import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2;
import org.matrix.androidsdk.data.cryptostore.IMXCryptoStore;
import org.matrix.androidsdk.util.JsonUtils;
import org.matrix.olm.OlmAccount;
import org.matrix.olm.OlmException;
import org.matrix.olm.OlmInboundGroupSession;
import org.matrix.olm.OlmMessage;
import org.matrix.olm.OlmOutboundGroupSession;
import org.matrix.olm.OlmSession;
import org.matrix.olm.OlmUtility;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MXOlmDevice {
private static final String LOG_TAG = "MXOlmDevice";
// Curve25519 key for the account.
private String mDeviceCurve25519Key;
// Ed25519 key for the account.
private String mDeviceEd25519Key;
// The store where crypto data is saved.
private final IMXCryptoStore mStore;
// The OLMKit account instance.
private OlmAccount mOlmAccount;
// The OLMKit utility instance.
private OlmUtility mOlmUtility;
// The outbound group session.
// They are not stored in 'store' to avoid to remember to which devices we sent the session key.
// Plus, in cryptography, it is good to refresh sessions from time to time.
// The key is the session id, the value the outbound group session.
private final HashMap<String, OlmOutboundGroupSession> mOutboundGroupSessionStore;
// Store a set of decrypted message indexes for each group session.
// This partially mitigates a replay attack where a MITM resends a group
// message into the room.
//
// The Matrix SDK exposes events through MXEventTimelines. A developer can open several
// timelines from a same room so that a message can be decrypted several times but from
// a different timeline.
// So, store these message indexes per timeline id.
//
// The first level keys are timeline ids.
// The second level keys are strings of form "<senderKey>|<session_id>|<message_index>"
// Values are true.
private final HashMap<String, HashMap<String, Boolean>> mInboundGroupSessionMessageIndexes;
/**
* inboundGroupSessionWithId error
*/
private MXCryptoError mInboundGroupSessionWithIdError = null;
/**
* Constructor
* @param store the used store
*/
public MXOlmDevice(IMXCryptoStore store) {
mStore = store;
// Retrieve the account from the store
mOlmAccount = mStore.getAccount();
if (null == mOlmAccount) {
// Else, create it
try {
mOlmAccount = new OlmAccount();
mStore.storeAccount(mOlmAccount);
} catch (Exception e) {
Log.e(LOG_TAG, "MXOlmDevice : cannot initialize mOlmAccount " + e.getMessage());
}
}
try {
mOlmUtility = new OlmUtility();
} catch (Exception e) {
Log.e(LOG_TAG, "## MXOlmDevice : OlmUtility failed with error " + e.getMessage());
mOlmUtility = null;
}
mOutboundGroupSessionStore = new HashMap<>();
try {
mDeviceCurve25519Key = mOlmAccount.identityKeys().get(OlmAccount.JSON_KEY_IDENTITY_KEY);
} catch (Exception e) {
Log.e(LOG_TAG, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_IDENTITY_KEY + " with error " + e.getMessage());
}
try {
mDeviceEd25519Key = mOlmAccount.identityKeys().get(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
} catch (Exception e) {
Log.e(LOG_TAG, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_FINGER_PRINT_KEY + " with error " + e.getMessage());
}
mInboundGroupSessionMessageIndexes = new HashMap();
}
/**
* Release the instance
*/
public void release() {
if (null != mOlmAccount) {
mOlmAccount.releaseAccount();
}
}
/**
* @return the Curve25519 key for the account.
*/
public String getDeviceCurve25519Key() {
return mDeviceCurve25519Key;
}
/**
* @return the Ed25519 key for the account.
*/
public String getDeviceEd25519Key() {
return mDeviceEd25519Key;
}
/**
* Signs a message with the ed25519 key for this account.
* @param message the message to be signed.
* @return the base64-encoded signature.
*/
private String signMessage(String message) {
try {
return mOlmAccount.signMessage(message);
} catch (Exception e) {
Log.e(LOG_TAG, "## signMessage() : failed " + e.getMessage());
}
return null;
}
/**
* Signs a JSON dictionary with the ed25519 key for this account.
* The signature is done on canonical version of the JSON.
* @param JSONDictionary the JSON to be signed.
* @return the base64-encoded signature
*/
public String signJSON(Map<String, Object> JSONDictionary) {
return signMessage(JsonUtils.getCanonicalizedJsonString(JSONDictionary));
}
/**
* @return The current (unused, unpublished) one-time keys for this account.
*/
public Map<String, Map<String, String>> getOneTimeKeys() {
try {
return mOlmAccount.oneTimeKeys();
} catch (Exception e) {
Log.e(LOG_TAG, "## getOneTimeKeys() : failed " + e.getMessage());
}
return null;
}
/**
* @return The maximum number of one-time keys the olm account can store.
*/
public long getMaxNumberOfOneTimeKeys() {
if (null != mOlmAccount) {
return mOlmAccount.maxOneTimeKeys();
} else {
return -1;
}
}
/**
* Marks all of the one-time keys as published.
*/
public void markKeysAsPublished() {
try {
mOlmAccount.markOneTimeKeysAsPublished();
mStore.storeAccount(mOlmAccount);
} catch (Exception e) {
Log.e(LOG_TAG, "## markKeysAsPublished() : failed " + e.getMessage());
}
}
/**
* Generate some new one-time keys
* @param numKeys number of keys to generate
*/
public void generateOneTimeKeys(int numKeys) {
try {
mOlmAccount.generateOneTimeKeys(numKeys);
mStore.storeAccount(mOlmAccount);
} catch (Exception e) {
Log.e(LOG_TAG, "## generateOneTimeKeys() : failed " + e.getMessage());
}
}
/**
* Generate a new outbound session.
* The new session will be stored in the MXStore.
* @param theirIdentityKey the remote user's Curve25519 identity key
* @param theirOneTimeKey the remote user's one-time Curve25519 key
* @return the session id for the outbound session. @TODO OLMSession?
*/
public String createOutboundSession(String theirIdentityKey, String theirOneTimeKey) {
Log.d(LOG_TAG, "## createOutboundSession() ; theirIdentityKey " + theirIdentityKey + " theirOneTimeKey " + theirOneTimeKey);
OlmSession olmSession = null;
try {
olmSession = new OlmSession();
olmSession.initOutboundSession(mOlmAccount, theirIdentityKey, theirOneTimeKey);
mStore.storeSession(olmSession, theirIdentityKey);
String sessionIdentifier = olmSession.sessionIdentifier();
Log.d(LOG_TAG, "## createOutboundSession() ; olmSession.sessionIdentifier: " + sessionIdentifier);
return sessionIdentifier;
} catch (Exception e) {
Log.e(LOG_TAG, "## createOutboundSession() failed ; " + e.getMessage());
if (null != olmSession) {
olmSession.releaseSession();
}
}
return null;
}
/**
*
* Generate a new inbound session, given an incoming message.
* @param theirDeviceIdentityKey the remote user's Curve25519 identity key.
* @param messageType the message_type field from the received message (must be 0).
* @param ciphertext base64-encoded body from the received message.
* @return {{payload: string, session_id: string}} decrypted payload, andsession id of new session.
*/
public Map<String, String> createInboundSession(String theirDeviceIdentityKey, int messageType, String ciphertext) {
Log.d(LOG_TAG, "## createInboundSession() : " + theirDeviceIdentityKey);
OlmSession olmSession = null;
try {
try {
olmSession = new OlmSession();
olmSession.initInboundSessionFrom(mOlmAccount, theirDeviceIdentityKey, ciphertext);
} catch (Exception e) {
Log.d(LOG_TAG, "## createInboundSession() : the session creation failed " + e.getMessage());
return null;
}
Log.d(LOG_TAG, "## createInboundSession() : " + olmSession.sessionIdentifier());
try {
mOlmAccount.removeOneTimeKeys(olmSession);
mStore.storeAccount(mOlmAccount);
} catch (Exception e) {
Log.e(LOG_TAG, "## createInboundSession() : removeOneTimeKeys failed " + e.getMessage());
}
Log.d(LOG_TAG, "## createInboundSession() : ciphertext: " + ciphertext);
try {
Log.d(LOG_TAG, "## createInboundSession() :ciphertext: SHA256:" + mOlmUtility.sha256(URLEncoder.encode(ciphertext, "utf-8")));
} catch (Exception e) {
Log.e(LOG_TAG, "## createInboundSession() :ciphertext: cannot encode ciphertext");
}
OlmMessage olmMessage = new OlmMessage();
olmMessage.mCipherText = ciphertext;
olmMessage.mType = messageType;
String payloadString = null;
try {
payloadString = olmSession.decryptMessage(olmMessage);
mStore.storeSession(olmSession, theirDeviceIdentityKey);
} catch (Exception e) {
Log.d(LOG_TAG, "## createInboundSession() : decryptMessage failed " + e.getMessage());
}
HashMap<String, String> res = new HashMap<>();
if (!TextUtils.isEmpty(payloadString)) {
res.put("payload", payloadString);
}
String sessionIdentifier = olmSession.sessionIdentifier();
if (!TextUtils.isEmpty(sessionIdentifier)) {
res.put("session_id", sessionIdentifier);
}
return res;
} catch (Exception e) {
Log.e(LOG_TAG, "## createInboundSession() : OlmSession creation failed " + e.getMessage());
if (null != olmSession) {
olmSession.releaseSession();
}
}
return null;
}
/**
* Get a list of known session IDs for the given device.
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
* @return a list of known session ids for the device.
*/
public Set<String> getSessionIds(String theirDeviceIdentityKey) {
Map<String, OlmSession> map = mStore.getDeviceSessions(theirDeviceIdentityKey);
if (null != map) {
return map.keySet();
}
return null;
}
/**
* Get the right olm session id for encrypting messages to the given identity key.
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
* @return the session id, or nil if no established session.
*/
public String getSessionId(String theirDeviceIdentityKey) {
String sessionId = null;
Set<String> sessionIds = getSessionIds(theirDeviceIdentityKey);
if ((null != sessionIds) && (0 != sessionIds.size())) {
ArrayList<String> sessionIdsList = new ArrayList<>(sessionIds);
Collections.sort(sessionIdsList);
sessionId = sessionIdsList.get(0);
}
return sessionId;
}
/**
* Encrypt an outgoing message using an existing session.
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
* @param sessionId the id of the active session
* @param payloadString the payload to be encrypted and sent
* @return the cipher text
*/
public Map<String, Object> encryptMessage(String theirDeviceIdentityKey, String sessionId, String payloadString) {
HashMap<String, Object> res = null;
OlmMessage olmMessage;
OlmSession olmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId);
if (null != olmSession) {
try {
Log.d(LOG_TAG, "## encryptMessage() : olmSession.sessionIdentifier: " + olmSession.sessionIdentifier());
Log.d(LOG_TAG, "## encryptMessage() : payloadString: " + payloadString);
olmMessage = olmSession.encryptMessage(payloadString);
mStore.storeSession(olmSession, theirDeviceIdentityKey);
res = new HashMap<>();
res.put("body", olmMessage.mCipherText);
res.put("type", olmMessage.mType);
} catch (Exception e) {
Log.e(LOG_TAG, "## encryptMessage() : failed " + e.getMessage());
}
}
return res;
}
/**
* Decrypt an incoming message using an existing session.
* @param ciphertext the base64-encoded body from the received message.
* @param messageType message_type field from the received message.
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
* @param sessionId the id of the active session.
* @return the decrypted payload.
*/
public String decryptMessage(String ciphertext, int messageType, String sessionId, String theirDeviceIdentityKey) {
String payloadString = null;
OlmSession olmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId);
if (null != olmSession) {
OlmMessage olmMessage = new OlmMessage();
olmMessage.mCipherText = ciphertext;
olmMessage.mType = messageType;
try {
payloadString = olmSession.decryptMessage(olmMessage);
mStore.storeSession(olmSession, theirDeviceIdentityKey);
} catch (Exception e) {
Log.e(LOG_TAG, "## decryptMessage() : decryptMessage failed " + e.getMessage());
}
}
return payloadString;
}
/**
* Determine if an incoming messages is a prekey message matching an existing session.
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
* @param sessionId the id of the active session.
* @param messageType message_type field from the received message.
* @param ciphertext the base64-encoded body from the received message.
* @return YES if the received message is a prekey message which matchesthe given session.
*/
public boolean matchesSession(String theirDeviceIdentityKey, String sessionId, int messageType, String ciphertext) {
if (messageType != 0) {
return false;
}
OlmSession olmSession = getSessionForDevice(theirDeviceIdentityKey, sessionId);
return (null != olmSession) && olmSession.matchesInboundSession(ciphertext);
}
// Outbound group session
/**
* Generate a new outbound group session.
* @return the session id for the outbound session.
*/
public String createOutboundGroupSession() {
OlmOutboundGroupSession session = null;
try {
session = new OlmOutboundGroupSession();
mOutboundGroupSessionStore.put(session.sessionIdentifier(), session);
return session.sessionIdentifier();
} catch (Exception e) {
Log.e(LOG_TAG, "createOutboundGroupSession " + e.getMessage());
if (null != session) {
session.releaseSession();
}
}
return null;
}
/**
* Get the current session key of an outbound group session.
* @param sessionId the id of the outbound group session.
* @return the base64-encoded secret key.
*/
public String getSessionKey(String sessionId) {
if (!TextUtils.isEmpty(sessionId)) {
try {
return mOutboundGroupSessionStore.get(sessionId).sessionKey();
} catch (Exception e) {
Log.e(LOG_TAG, "## getSessionKey() : failed " + e.getMessage());
}
}
return null;
}
/**
* Get the current message index of an outbound group session.
* @param sessionId the id of the outbound group session.
* @return the current chain index.
*/
public int getMessageIndex(String sessionId) {
if (!TextUtils.isEmpty(sessionId)) {
return mOutboundGroupSessionStore.get(sessionId).messageIndex();
}
return 0;
}
/**
* Encrypt an outgoing message with an outbound group session.
* @param sessionId the id of the outbound group session.
* @param payloadString the payload to be encrypted and sent.
* @return ciphertext
*/
public String encryptGroupMessage(String sessionId, String payloadString) {
if (!TextUtils.isEmpty(sessionId) && !TextUtils.isEmpty(payloadString)) {
try {
return mOutboundGroupSessionStore.get(sessionId).encryptMessage(payloadString);
} catch (Exception e) {
Log.e(LOG_TAG, "## encryptGroupMessage() : failed " + e.getMessage());
}
}
return null;
}
// Inbound group session
/**
* Add an inbound group session to the session store.
* @param sessionId the session identifier.
* @param sessionKey base64-encoded secret key.
* @param roomId the id of the room in which this session will be used.
* @param senderKey the base64-encoded curve25519 key of the sender.
* @param keysClaimed Other keys the sender claims.
* @return true if the operation succeeds.
*/
public boolean addInboundGroupSession(String sessionId, String sessionKey, String roomId, String senderKey, Map<String, String> keysClaimed) {
if (null != getInboundGroupSession(sessionId, senderKey, roomId)) {
// If we already have this session, consider updating it
Log.e(LOG_TAG, "## addInboundGroupSession() : Update for megolm session " + senderKey + "/" + sessionId);
// For now we just ignore updates. TODO: implement something here
return false;
}
MXOlmInboundGroupSession2 session = new MXOlmInboundGroupSession2(sessionKey);
// sanity check
if (null == session.mSession) {
Log.e(LOG_TAG, "## addInboundGroupSession : invalid session");
return false;
}
try {
if (!TextUtils.equals(session.mSession.sessionIdentifier(), sessionId)) {
Log.e(LOG_TAG, "## addInboundGroupSession : ERROR: Mismatched group session ID from senderKey: " + senderKey);
return false;
}
} catch (Exception e) {
Log.e(LOG_TAG, "## addInboundGroupSession : sessionIdentifier') failed " + e.getMessage());
return false;
}
session.mSenderKey = senderKey;
session.mRoomId = roomId;
session.mKeysClaimed = keysClaimed;
mStore.storeInboundGroupSession(session);
return true;
}
/**
* Import an inbound group session to the session store.
* @param exportedSessionMap the exported session map
* @return the imported session if the operation succeeds.
*/
public MXOlmInboundGroupSession2 importInboundGroupSession(Map<String, Object> exportedSessionMap) {
String sessionId = (String)exportedSessionMap.get("session_id");
String senderKey = (String)exportedSessionMap.get("sender_key");
String roomId = (String)exportedSessionMap.get("room_id");
if (null != getInboundGroupSession(sessionId, senderKey, roomId)) {
// If we already have this session, consider updating it
Log.e(LOG_TAG, "## importInboundGroupSession() : Update for megolm session " + senderKey + "/" + sessionId);
// For now we just ignore updates. TODO: implement something here
return null;
}
MXOlmInboundGroupSession2 session = null;
try {
session = new MXOlmInboundGroupSession2(exportedSessionMap);
} catch (Exception e) {
Log.e(LOG_TAG, "## importInboundGroupSession() : Update for megolm session " + senderKey + "/" + sessionId);
}
// sanity check
if ((null == session) || (null == session.mSession)) {
Log.e(LOG_TAG, "## importInboundGroupSession : invalid session");
return null;
}
try {
if (!TextUtils.equals(session.mSession.sessionIdentifier(), sessionId)) {
Log.e(LOG_TAG, "## importInboundGroupSession : ERROR: Mismatched group session ID from senderKey: " + senderKey);
return null;
}
} catch (Exception e) {
Log.e(LOG_TAG, "## importInboundGroupSession : sessionIdentifier') failed " + e.getMessage());
return null;
}
mStore.storeInboundGroupSession(session);
return session;
}
/**
* Remove an inbound group session
* @param sessionId the session identifier.
* @param sessionKey base64-encoded secret key.
*/
public void removeInboundGroupSession(String sessionId, String sessionKey) {
if ((null != sessionId) && (null != sessionKey)) {
mStore.removeInboundGroupSession(sessionId, sessionKey);
}
}
/**
* Decrypt a received message with an inbound group session.
* @param body the base64-encoded body of the encrypted message.
* @param roomId theroom in which the message was received.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @param sessionId the session identifier.
* @param senderKey the base64-encoded curve25519 key of the sender.
* @return the decrypting result. Nil if the sessionId is unknown.
*/
public MXDecryptionResult decryptGroupMessage(String body, String roomId, String timeline, String sessionId, String senderKey) {
MXDecryptionResult result = new MXDecryptionResult();
MXOlmInboundGroupSession2 session = getInboundGroupSession(sessionId, senderKey, roomId);
if (null != session) {
// 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 (TextUtils.equals(roomId, session.mRoomId)) {
String errorMessage = "";
OlmInboundGroupSession.DecryptMessageResult decryptResult = null;
try {
decryptResult = session.mSession.decryptMessage(body);
} catch (Exception e) {
Log.e(LOG_TAG, "## decryptGroupMessage () : decryptMessage failed " + e.getMessage());
errorMessage = e.getMessage();
}
if (null != decryptResult) {
if (null != timeline) {
if (!mInboundGroupSessionMessageIndexes.containsKey(timeline)) {
mInboundGroupSessionMessageIndexes.put(timeline, new HashMap<String, Boolean>());
}
String messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex;
if (null != mInboundGroupSessionMessageIndexes.get(timeline).get(messageIndexKey)) {
String reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex);
Log.e(LOG_TAG,"## decryptGroupMessage() : " + reason);
result.mCryptoError = new MXCryptoError(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason);
return result;
}
mInboundGroupSessionMessageIndexes.get(timeline).put(messageIndexKey, true);
}
mStore.storeInboundGroupSession(session);
try {
JsonParser parser = new JsonParser();
result.mPayload = parser.parse(JsonUtils.convertFromUTF8(decryptResult.mDecryptedMessage));
} catch (Exception e) {
Log.e(LOG_TAG, "## decryptGroupMessage() : RLEncoder.encode failed " + e.getMessage());
return null;
}
if (null == result.mPayload) {
Log.e(LOG_TAG, "## decryptGroupMessage() : fails to parse the payload");
return null;
}
result.mKeysClaimed = session.mKeysClaimed;
// The sender must have had the senderKey to persuade us to save the
// session.
HashMap<String, String> map = new HashMap<>();
map.put("curve25519", senderKey);
result.mKeysProved = map;
} else {
result.mCryptoError = new MXCryptoError(MXCryptoError.OLM_ERROR_CODE, errorMessage, null);
Log.e(LOG_TAG, "## decryptGroupMessage() : failed to decode the message");
}
} else {
String reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.mRoomId);
Log.e(LOG_TAG, "## decryptGroupMessage() : " + reason);
result.mCryptoError = new MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason);
}
}
else {
result.mCryptoError = mInboundGroupSessionWithIdError;
Log.e(LOG_TAG, "## decryptGroupMessage() : Cannot retrieve inbound group session " + sessionId);
}
return result;
}
/**
* Reset replay attack data for the given timeline.
* @param timeline the id of the timeline.
*/
public void resetReplayAttackCheckInTimeline(String timeline) {
if (null != timeline) {
mInboundGroupSessionMessageIndexes.remove(timeline);
}
}
// Utilities
/**
* Verify an ed25519 signature on a JSON object.
* @param key the ed25519 key.
* @param JSONDictinary the JSON object which was signed.
* @param signature the base64-encoded signature to be checked.
* @exception Exception the exception
*/
public void verifySignature(String key, Map<String, Object> JSONDictinary, String signature) throws Exception {
// Check signature on the canonical version of the JSON
mOlmUtility.verifyEd25519Signature(signature, key, JsonUtils.getCanonicalizedJsonString(JSONDictinary));
}
/**
* Calculate the SHA-256 hash of the input and encodes it as base64.
* @param message the message to hash.
* @return the base64-encoded hash value.
*/
public String sha256(String message) {
return mOlmUtility.sha256(JsonUtils.convertToUTF8(message));
}
/**
* Search an OlmSession
* @param theirDeviceIdentityKey the device key
* @param sessionId the session Id
* @return the olm session
*/
private OlmSession getSessionForDevice(String theirDeviceIdentityKey, String sessionId) {
// sanity check
if (!TextUtils.isEmpty(theirDeviceIdentityKey) && !TextUtils.isEmpty(sessionId)) {
Map<String, OlmSession> map = mStore.getDeviceSessions(theirDeviceIdentityKey);
if (null != map) {
return map.get(sessionId);
}
}
return null;
}
/**
* Extract an InboundGroupSession from the session store and do some check.
* mInboundGroupSessionWithIdError describes the failure reason.
* @param roomId the room where the sesion is used.
* @param sessionId the session identifier.
* @param senderKey the base64-encoded curve25519 key of the sender.
* @return the inbound group session.
*/
private MXOlmInboundGroupSession2 getInboundGroupSession(String sessionId, String senderKey, String roomId) {
mInboundGroupSessionWithIdError = null;
MXOlmInboundGroupSession2 session = mStore.getInboundGroupSession(sessionId, senderKey);
if (null != session) {
// 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 (!TextUtils.equals(roomId, session.mRoomId)) {
String errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.mRoomId);
Log.e(LOG_TAG, "## getInboundGroupSession() : " + errorDescription);
mInboundGroupSessionWithIdError = new MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, errorDescription);
}
} else {
Log.e(LOG_TAG, "## getInboundGroupSession() : Cannot retrieve inbound group session " + sessionId);
mInboundGroupSessionWithIdError = new MXCryptoError(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON, null);
}
return session;
}
}