aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYannick LE COLLEN <yannick@matrix.org>2017-01-18 15:33:14 +0100
committerRichard van der Hoff <github@rvanderhoff.org.uk>2017-01-18 14:33:14 +0000
commit3c02c1547c09f5934c2d5620790545701d2034eb (patch)
tree3437f5ef4612800ace23861e9983f6d78bf8b336
parent1761730db8c54a949ad6d12522de8eee2e0a50be (diff)
Android: Add wrappers for export/import of inbound group sessions
-rw-r--r--android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java102
-rw-r--r--android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java3
-rw-r--r--android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java111
-rw-r--r--android/olm-sdk/src/main/jni/olm_inbound_group_session.cpp151
-rw-r--r--android/olm-sdk/src/main/jni/olm_inbound_group_session.h7
5 files changed, 361 insertions, 13 deletions
diff --git a/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java b/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java
index 9be6375..69eb0e8 100644
--- a/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java
+++ b/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java
@@ -521,5 +521,107 @@ public class OlmGroupSessionTest {
assertTrue(0!=EXPECTED_ERROR_MESSAGE.length());
assertTrue(EXPECTED_ERROR_MESSAGE.equals(exceptionMessage));
}
+
+
+ /**
+ * Test the import/export functions.<br>
+ **/
+ @Test
+ public void test20TestInboundGroupSessionImportExport() {
+
+ String sessionKey = "AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" +
+ "DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" +
+ "U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMw0bdg1BDq4Px/slBow06q8n/B9WBfw" +
+ "WYyNOB8DlUmXGGwrFmaSb9bR/eY8xgERrxmP07hFmD9uqA2p8PMHdnV5ysmgufE6oLZ5+" +
+ "8/mWQOW3VVTnDIlnwd8oHUYRuk8TCQ";
+
+ String message = "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnRORG9T3D" +
+ "XFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD";
+
+
+ OlmInboundGroupSession inboundGroupSession = null;
+
+ try {
+ inboundGroupSession = new OlmInboundGroupSession(sessionKey);
+ } catch (Exception e) {
+ assertTrue("OlmInboundGroupSession failed " + e.getMessage(), false);
+ }
+
+ boolean isVerified = false;
+
+ try {
+ isVerified = inboundGroupSession.isVerified();
+ } catch (Exception e) {
+ assertTrue("isVerified failed " + e.getMessage(), false);
+ }
+
+ assertTrue(isVerified);
+
+ OlmInboundGroupSession.DecryptMessageResult result = null;
+
+ try {
+ result = inboundGroupSession.decryptMessage(message);
+ } catch (Exception e) {
+ assertTrue("decryptMessage failed " + e.getMessage(), false);
+ }
+
+ assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
+ assertTrue(0 == result.mIndex);
+
+ String export = null;
+
+ try {
+ export = inboundGroupSession.export(0);
+ } catch (Exception e) {
+ assertTrue("export failed " + e.getMessage(), false);
+ }
+ assertTrue(!TextUtils.isEmpty(export));
+
+ long index = -1;
+ try {
+ index = inboundGroupSession.getFirstKnownIndex();
+ } catch (Exception e) {
+ assertTrue("getFirstKnownIndex failed " + e.getMessage(), false);
+ }
+ assertTrue(index >=0);
+
+ inboundGroupSession.releaseSession();
+ inboundGroupSession = null;
+
+ OlmInboundGroupSession inboundGroupSession2 = null;
+
+ try {
+ inboundGroupSession2 = inboundGroupSession.importSession(export);
+ } catch (Exception e) {
+ assertTrue("OlmInboundGroupSession failed " + e.getMessage(), false);
+ }
+
+ try {
+ isVerified = inboundGroupSession2.isVerified();
+ } catch (Exception e) {
+ assertTrue("isVerified failed " + e.getMessage(), false);
+ }
+
+ assertFalse(isVerified);
+
+ result = null;
+ try {
+ result = inboundGroupSession2.decryptMessage(message);
+ } catch (Exception e) {
+ assertTrue("decryptMessage failed " + e.getMessage(), false);
+ }
+
+ assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
+ assertTrue(0 == result.mIndex);
+
+ try {
+ isVerified = inboundGroupSession2.isVerified();
+ } catch (Exception e) {
+ assertTrue("isVerified failed " + e.getMessage(), false);
+ }
+
+ assertTrue(isVerified);
+ inboundGroupSession2.releaseSession();
+ }
}
diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java
index b0b1a6a..33ac49e 100644
--- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java
+++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java
@@ -40,6 +40,9 @@ public class OlmException extends IOException {
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 201;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_IDENTIFIER = 202;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_DECRYPT_SESSION = 203;
+ public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_FIRST_KNOWN_INDEX = 204;
+ public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_IS_VERIFIED = 205;
+ public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_EXPORT = 206;
public static final int EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION = 300;
public static final int EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION = 301;
diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java
index 571bddb..f5173c3 100644
--- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java
+++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java
@@ -59,12 +59,24 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
* @throws OlmException constructor failure
*/
public OlmInboundGroupSession(String aSessionKey) throws OlmException {
+ this(aSessionKey, false);
+ }
+
+ /**
+ * Constructor.<br>
+ * Create and save a new native session instance ID and start a new inbound group session.
+ * The session key parameter is retrieved from an outbound group session.
+ * @param aSessionKey session key
+ * @param isImported true when the session key has been retrieved from a backup
+ * @throws OlmException constructor failure
+ */
+ private OlmInboundGroupSession(String aSessionKey, boolean isImported) throws OlmException {
if (TextUtils.isEmpty(aSessionKey)) {
Log.e(LOG_TAG, "## initInboundGroupSession(): invalid session key");
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, "invalid session key");
} else {
try {
- mNativeId = createNewSessionJni(aSessionKey.getBytes("UTF-8"));
+ mNativeId = createNewSessionJni(aSessionKey.getBytes("UTF-8"), isImported);
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, e.getMessage());
}
@@ -76,9 +88,20 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmInboundGroupSession* to jlong) platform independent.
* @param aSessionKeyBuffer session key from an outbound session
+ * @param isImported true when the session key has been retrieved from a backup
* @return the initialized OlmInboundGroupSession* instance or throw an exception it fails.
**/
- private native long createNewSessionJni(byte[] aSessionKeyBuffer);
+ private native long createNewSessionJni(byte[] aSessionKeyBuffer, boolean isImported);
+
+ /**
+ * Create an OlmInboundGroupSession from its exported session data.
+ * @param exported the exported data
+ * @return the created OlmException
+ * @throws OlmException the failure reason
+ */
+ public static OlmInboundGroupSession importSession(String exported) throws OlmException {
+ return new OlmInboundGroupSession(exported, true);
+ }
/**
* Release native session and invalid its JAVA reference counter part.<br>
@@ -95,7 +118,7 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
* Destroy the corresponding OLM inbound group session native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
- * See {@link #createNewSessionJni(byte[])}.
+ * See {@link #createNewSessionJni(byte[], boolean)}.
*/
private native void releaseSessionJni();
@@ -129,6 +152,86 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
private native byte[] sessionIdentifierJni();
/**
+ * Provides the first known index.
+ * @return the first known index.
+ * @throws OlmException the failure reason
+ */
+ public long getFirstKnownIndex() throws OlmException {
+ long index = 0;
+
+ try {
+ index = firstKnownIndexJni();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "## getFirstKnownIndex() failed " + e.getMessage());
+ throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_FIRST_KNOWN_INDEX, e.getMessage());
+ }
+
+ return index;
+ }
+
+ /**
+ * Provides the first known index.
+ * An exception is thrown if the operation fails.
+ * @return the first known index.
+ */
+ private native long firstKnownIndexJni();
+
+ /**
+ * Tells if the session is verified.
+ * @return true if the session is verified
+ * @throws OlmException the failure reason
+ */
+ public boolean isVerified() throws OlmException {
+ boolean isVerified;
+
+ try {
+ isVerified = isVerifiedJni();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "## isVerified() failed " + e.getMessage());
+ throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_IS_VERIFIED, e.getMessage());
+ }
+
+ return isVerified;
+ }
+
+ /**
+ * Tells if the session is verified.
+ * @return true if the session is verified
+ */
+ private native boolean isVerifiedJni();
+
+ /**
+ * Export the session from a message index as String.
+ * @param messageIndex the message index
+ * @return the session as String
+ * @throws OlmException the failure reason
+ */
+ public String export(long messageIndex) throws OlmException {
+ String result = null;
+
+ try {
+ byte[] bytesBuffer = exportJni(messageIndex);
+
+ if (null != bytesBuffer) {
+ result = new String(bytesBuffer, "UTF-8");
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "## export() failed " + e.getMessage());
+ throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_EXPORT, e.getMessage());
+ }
+
+ return result;
+ }
+
+ /**
+ * Exports the session as byte array from a message index
+ * An exception is thrown if the operation fails.
+ * @param messageIndex key used to encrypt the serialized session data
+ * @return the session saved as bytes array
+ */
+ private native byte[] exportJni(long messageIndex);
+
+ /**
* Decrypt the message passed in parameter.<br>
* In case of error, null is returned and an error message description is provided in aErrorMsg.
* @param aEncryptedMsg the message to be decrypted
@@ -156,7 +259,7 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
* Decrypt a message.
* An exception is thrown if the operation fails.
* @param aEncryptedMsg the encrypted message
- * @param aDecryptMessageResult the decryptMessage informaton
+ * @param aDecryptMessageResult the decryptMessage information
* @return the decrypted message
*/
private native byte[] decryptMessageJni(byte[] aEncryptedMsg, DecryptMessageResult aDecryptMessageResult);
diff --git a/android/olm-sdk/src/main/jni/olm_inbound_group_session.cpp b/android/olm-sdk/src/main/jni/olm_inbound_group_session.cpp
index 23910bb..114b7cd 100644
--- a/android/olm-sdk/src/main/jni/olm_inbound_group_session.cpp
+++ b/android/olm-sdk/src/main/jni/olm_inbound_group_session.cpp
@@ -54,9 +54,10 @@ JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmInboundGroupSession* => jlong) platform independent.
* @param aSessionKeyBuffer session key from an outbound session
+ * @param isImported true when the session key has been retrieved from a backup
* @return the initialized OlmInboundGroupSession* instance or throw an exception it fails.
**/
-JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer)
+JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer, jboolean isImported)
{
const char* errorMessage = NULL;
OlmInboundGroupSession* sessionPtr = NULL;
@@ -92,7 +93,18 @@ JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *
size_t sessionKeyLength = (size_t)env->GetArrayLength(aSessionKeyBuffer);
LOGD(" ## createNewSessionJni(): sessionKeyLength=%lu", static_cast<long unsigned int>(sessionKeyLength));
- size_t sessionResult = olm_init_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
+ size_t sessionResult;
+
+ if (JNI_FALSE == isImported)
+ {
+ LOGD(" ## createNewSessionJni(): init");
+ sessionResult = olm_init_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
+ }
+ else
+ {
+ LOGD(" ## createNewSessionJni(): import");
+ sessionResult = olm_import_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
+ }
if (sessionResult == olm_error())
{
@@ -112,18 +124,12 @@ JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *
if (errorMessage)
{
- env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
- }
-
- if (errorMessage)
- {
// release the allocated session
if (sessionPtr)
{
olm_clear_inbound_group_session(sessionPtr);
free(sessionPtr);
}
-
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
@@ -326,6 +332,135 @@ JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEn
return decryptedMsgBuffer;
}
+/**
+ * Provides the first known index.
+ * An exception is thrown if the operation fails.
+ * @return the first known index
+ */
+JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(firstKnownIndexJni)(JNIEnv *env, jobject thiz)
+{
+ const char* errorMessage = NULL;
+ OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
+ long returnValue = 0;
+
+ LOGD("## firstKnownIndexJni(): inbound group session IN");
+
+ if (!sessionPtr)
+ {
+ LOGE(" ## firstKnownIndexJni(): failure - invalid inbound group session instance");
+ errorMessage = "invalid inbound group session instance";
+ }
+ else
+ {
+ returnValue = olm_inbound_group_session_first_known_index(sessionPtr);
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return returnValue;
+}
+
+/**
+ * Tells if the session is verified.
+ * An exception is thrown if the operation fails.
+ * @return true if the session is verified
+ */
+JNIEXPORT jboolean OLM_INBOUND_GROUP_SESSION_FUNC_DEF(isVerifiedJni)(JNIEnv *env, jobject thiz)
+{
+ const char* errorMessage = NULL;
+ OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
+ jboolean returnValue = JNI_FALSE;
+
+ LOGD("## isVerifiedJni(): inbound group session IN");
+
+ if (!sessionPtr)
+ {
+ LOGE(" ## isVerifiedJni(): failure - invalid inbound group session instance");
+ errorMessage = "invalid inbound group session instance";
+ }
+ else
+ {
+ LOGE(" ## isVerifiedJni(): faaa %d", olm_inbound_group_session_is_verified(sessionPtr));
+
+ returnValue = (0 != olm_inbound_group_session_is_verified(sessionPtr)) ? JNI_TRUE : JNI_FALSE;
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return returnValue;
+}
+
+/**
+ * Exports the session as byte array from a message index
+ * An exception is thrown if the operation fails.
+ * @param messageIndex key used to encrypt the serialized session data
+ * @return the session saved as bytes array
+ **/
+JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(exportJni)(JNIEnv *env, jobject thiz, jlong messageIndex) {
+ jbyteArray exportedByteArray = 0;
+ const char* errorMessage = NULL;
+ OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
+
+ LOGD("## exportJni(): inbound group session IN");
+
+ if (!sessionPtr)
+ {
+ LOGE(" ## exportJni (): failure - invalid inbound group session instance");
+ errorMessage = "invalid inbound group session instance";
+ }
+ else
+ {
+ size_t length = olm_export_inbound_group_session_length(sessionPtr);
+
+ LOGD(" ## exportJni(): length =%lu", static_cast<long unsigned int>(length));
+
+ void *bufferPtr = malloc(length * sizeof(uint8_t));
+
+ if (!bufferPtr)
+ {
+ LOGE(" ## exportJni(): failure - pickledPtr buffer OOM");
+ errorMessage = "pickledPtr buffer OOM";
+ }
+ else
+ {
+ size_t result = olm_export_inbound_group_session(sessionPtr,
+ (uint8_t*)bufferPtr,
+ length,
+ (long) messageIndex);
+
+ if (result == olm_error())
+ {
+ errorMessage = olm_inbound_group_session_last_error(sessionPtr);
+ LOGE(" ## exportJni(): failure - olm_export_inbound_group_session() Msg=%s", errorMessage);
+ }
+ else
+ {
+ LOGD(" ## exportJni(): success - result=%lu buffer=%.*s", static_cast<long unsigned int>(result), static_cast<int>(length), static_cast<char*>(bufferPtr));
+
+ exportedByteArray = env->NewByteArray(length);
+ env->SetByteArrayRegion(exportedByteArray, 0 , length, (jbyte*)bufferPtr);
+
+ // clean before leaving
+ memset(bufferPtr, 0, length);
+ }
+
+ free(bufferPtr);
+ }
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return exportedByteArray;
+}
/**
* Serialize and encrypt session instance into a base64 string.<br>
diff --git a/android/olm-sdk/src/main/jni/olm_inbound_group_session.h b/android/olm-sdk/src/main/jni/olm_inbound_group_session.h
index 00990dd..be4c013 100644
--- a/android/olm-sdk/src/main/jni/olm_inbound_group_session.h
+++ b/android/olm-sdk/src/main/jni/olm_inbound_group_session.h
@@ -30,11 +30,16 @@ extern "C" {
// session creation/destruction
JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
-JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer);
+JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer, jboolean isImported);
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aEncryptedMsg, jobject aDecryptIndex);
+JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(firstKnownIndexJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT jboolean OLM_INBOUND_GROUP_SESSION_FUNC_DEF(isVerifiedJni)(JNIEnv *env, jobject thiz);
+
+JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(exportJni)(JNIEnv *env, jobject thiz, jlong messageIndex);
+
// serialization
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);