From fae857582c445ce89f527f6331aeac4eb66bc692 Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Fri, 21 Oct 2016 19:05:20 +0200 Subject: Add serialization for outbound group session --- .../java/org/matrix/olm/OlmGroupSessionTest.java | 17 ++- .../src/main/java/org/matrix/olm/OlmException.java | 4 +- .../org/matrix/olm/OlmInboundGroupSession.java | 2 +- .../src/main/java/org/matrix/olm/OlmManager.java | 11 +- .../org/matrix/olm/OlmOutboundGroupSession.java | 123 +++++++++++++++- .../src/main/java/org/matrix/olm/OlmSession.java | 4 +- .../OlmLibSdk/olm-sdk/src/main/jni/Android.mk | 3 +- .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp | 1 - .../src/main/jni/olm_outbound_group_session.cpp | 162 +++++++++++++++++++++ .../src/main/jni/olm_outbound_group_session.h | 3 + .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp | 1 - 11 files changed, 318 insertions(+), 13 deletions(-) (limited to 'java/android/OlmLibSdk/olm-sdk') diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java index e488ae0..d49ebd4 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java @@ -183,16 +183,27 @@ public class OlmGroupSessionTest { outboundGroupSessionSerial = (OlmOutboundGroupSession) objectInput.readObject(); objectInput.close(); - // get sessions IDs + // get sessions keys String sessionKeyRef = outboundGroupSessionRef.sessionKey(); String sessionKeySerial = outboundGroupSessionSerial.sessionKey(); - // session ID sanity check + // session keys sanity check assertFalse(TextUtils.isEmpty(sessionKeyRef)); assertFalse(TextUtils.isEmpty(sessionKeySerial)); - // session IDs comparison + // session keys comparison assertTrue(sessionKeyRef.equals(sessionKeySerial)); + + // get sessions IDs + String sessionIdRef = outboundGroupSessionRef.sessionIdentifier(); + String sessionIdSerial = outboundGroupSessionSerial.sessionIdentifier(); + + // session ID sanity check + assertFalse(TextUtils.isEmpty(sessionIdRef)); + assertFalse(TextUtils.isEmpty(sessionIdSerial)); + + // session IDs comparison + assertTrue(sessionIdRef.equals(sessionIdSerial)); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "## test03SessionSerialization(): Exception FileNotFoundException Msg=="+e.getMessage()); diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmException.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmException.java index 280dce1..d74f6e3 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmException.java +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmException.java @@ -31,6 +31,8 @@ public class OlmException extends Exception { public static final int EXCEPTION_CODE_SESSION_DESERIALIZATION = 7; public static final int EXCEPTION_CODE_INIT_ACCOUNT_CREATION = 8; public static final int EXCEPTION_CODE_INIT_SESSION_CREATION = 9; + public static final int EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_SERIALIZATION = 10; + public static final int EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_DESERIALIZATION = 11; // exception human readable messages public static final String EXCEPTION_MSG_NEW_OUTBOUND_GROUP_SESSION = "failed to create a new outbound group Session"; @@ -39,7 +41,7 @@ public class OlmException extends Exception { public static final String EXCEPTION_MSG_INIT_INBOUND_GROUP_SESSION = "failed to initialize a new inbound group Session"; public static final String EXCEPTION_MSG_INIT_NEW_ACCOUNT_DESERIALIZATION = "initNewAccount() failure"; public static final String EXCEPTION_MSG_INIT_ACCOUNT_DESERIALIZATION = "initWithSerializedData() failure"; - public static final String EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION = "invalid deserialized parameters"; + public static final String EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION = "invalid de-serialized parameters"; public static final String EXCEPTION_MSG_INIT_ACCOUNT_CREATION = "Account constructor failure"; public static final String EXCEPTION_MSG_INIT_SESSION_CREATION = "Session constructor failure"; diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java index d7d9a1c..e78e259 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java @@ -26,7 +26,7 @@ import android.util.Log; import java.io.Serializable; public class OlmInboundGroupSession implements Serializable { - + private static final long serialVersionUID = -772028491251653253L; private static final String LOG_TAG = "OlmInboundGroupSession"; /** session raw pointer value returned by JNI.
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java index 55b9910..403b4b3 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java @@ -17,10 +17,17 @@ package org.matrix.olm; +import android.util.Log; + public class OlmManager { + private static final String LOG_TAG = "OlmManager"; - static { - java.lang.System.loadLibrary("olm"); + public OlmManager() { + try { + java.lang.System.loadLibrary("olm"); + } catch(UnsatisfiedLinkError e) { + Log.e(LOG_TAG,"Exception loadLibrary() - Msg="+e.getMessage()); + } } /** diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java index 7c2c42b..7fdf405 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java @@ -18,8 +18,15 @@ package org.matrix.olm; import android.text.TextUtils; +import android.util.Log; -public class OlmOutboundGroupSession { +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class OlmOutboundGroupSession implements Serializable { + private static final long serialVersionUID = -3133097431283604416L; private static final String LOG_TAG = "OlmOutboundGroupSession"; /** session raw pointer value returned by JNI.
@@ -53,6 +60,120 @@ public class OlmOutboundGroupSession { } } + /** + * Kick off the serialization mechanism. + * @param aOutStream output stream for serializing + * @throws IOException + * @throws OlmException + */ + private void writeObject(ObjectOutputStream aOutStream) throws IOException, OlmException { + aOutStream.defaultWriteObject(); + + // generate serialization key + String key = OlmUtility.getRandomKey(); + + // compute pickle string + StringBuffer errorMsg = new StringBuffer(); + String pickledData = serializeDataWithKey(key, errorMsg); + + if(null == pickledData) { + throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_SERIALIZATION, String.valueOf(errorMsg)); + } else { + aOutStream.writeObject(key); + aOutStream.writeObject(pickledData); + } + } + + /** + * Kick off the deserialization mechanism. + * @param aInStream + * @throws IOException + * @throws ClassNotFoundException + * @throws OlmException + */ + private void readObject(ObjectInputStream aInStream) throws IOException, ClassNotFoundException, OlmException { + aInStream.defaultReadObject(); + StringBuffer errorMsg = new StringBuffer(); + + String key = (String) aInStream.readObject(); + String pickledData = (String) aInStream.readObject(); + + if(TextUtils.isEmpty(key)) { + throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_DESERIALIZATION, OlmException.EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION+" key"); + + } else if(TextUtils.isEmpty(pickledData)) { + throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_DESERIALIZATION, OlmException.EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION+" pickle"); + + } else if(!initNewSession()) { + throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_DESERIALIZATION, OlmException.EXCEPTION_MSG_INIT_NEW_ACCOUNT_DESERIALIZATION); + + } else if(!initWithSerializedData(pickledData, key, errorMsg)) { + releaseSession(); // prevent memory leak + throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_DESERIALIZATION, String.valueOf(errorMsg)); + + } else { + Log.d(LOG_TAG,"## readObject(): success"); + } + } + + /** + * Return a session as a base64 string.
+ * The account is serialized and encrypted with aKey. + * In case of failure, an error human readable + * description is provide in aErrorMsg. + * @param aKey encryption key + * @param aErrorMsg error message description + * @return pickled base64 string if operation succeed, null otherwise + */ + private String serializeDataWithKey(String aKey, StringBuffer aErrorMsg) { + String pickleRetValue = null; + + // sanity check + if(null == aErrorMsg) { + Log.e(LOG_TAG,"## serializeDataWithKey(): invalid parameter - aErrorMsg=null"); + } else if(TextUtils.isEmpty(aKey)) { + aErrorMsg.append("Invalid input parameters in serializeDataWithKey()"); + } else { + aErrorMsg.setLength(0); + pickleRetValue = serializeDataWithKeyJni(aKey, aErrorMsg); + } + + return pickleRetValue; + } + private native String serializeDataWithKeyJni(String aKey, StringBuffer aErrorMsg); + + + /** + * Loads an account from a pickled base64 string.
+ * See {@link #serializeDataWithKey(String, StringBuffer)} + * @param aSerializedData pickled account in a base64 string format + * @param aKey key used to encrypted + * @param aErrorMsg error message description + * @return true if operation succeed, false otherwise + */ + private boolean initWithSerializedData(String aSerializedData, String aKey, StringBuffer aErrorMsg) { + boolean retCode = false; + String jniError; + + if(null == aErrorMsg) { + Log.e(LOG_TAG, "## initWithSerializedData(): invalid input error parameter"); + } else { + aErrorMsg.setLength(0); + + if (TextUtils.isEmpty(aSerializedData) || TextUtils.isEmpty(aKey)) { + Log.e(LOG_TAG, "## initWithSerializedData(): invalid input parameters"); + } else if (null == (jniError = initWithSerializedDataJni(aSerializedData, aKey))) { + retCode = true; + } else { + aErrorMsg.append(jniError); + } + } + + return retCode; + } + private native String initWithSerializedDataJni(String aSerializedData, String aKey); + + /** * Release native session and invalid its JAVA reference counter part.
* Public API for {@link #releaseSessionJni()}. diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java index ca74352..eaf6110 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java @@ -97,7 +97,7 @@ public class OlmSession implements Serializable { } /** - * Return an account as a base64 string.
+ * Return a session as a base64 string.
* The account is serialized and encrypted with aKey. * In case of failure, an error human readable * description is provide in aErrorMsg. @@ -124,7 +124,7 @@ public class OlmSession implements Serializable { /** - * Loads an account from a pickled base64 string.
+ * Loads a session from a pickled base64 string.
* See {@link #serializeDataWithKey(String, StringBuffer)} * @param aSerializedData pickled account in a base64 string format * @param aKey key used to encrypted diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk index 92a9359..7a8f20b 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk @@ -47,7 +47,8 @@ olm_account.cpp \ olm_session.cpp \ olm_jni_helper.cpp \ olm_inbound_group_session.cpp \ -olm_outbound_group_session.cpp +olm_outbound_group_session.cpp \ +olm_utility.cpp LOCAL_LDLIBS := -llog diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp index fd3a977..1a1b360 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp @@ -642,7 +642,6 @@ JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(initWithSerializedDataJni)(JNIEnv *env, j { LOGD(" ## initWithSerializedDataJni(): success - result=%lu ", result); } - } // free alloc diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp index 55c6539..67bf8bd 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp @@ -330,4 +330,166 @@ JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv } +/** +* Serialize and encrypt session instance into a base64 string.
+* @param aKey key used to encrypt the serialized session data +* @param[out] aErrorMsg error message set if operation failed +* @return a base64 string if operation succeed, null otherwise +**/ +JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(serializeDataWithKeyJni)(JNIEnv *env, jobject thiz, jstring aKey, jobject aErrorMsg) +{ + jstring pickledDataRetValue = 0; + jclass errorMsgJClass = 0; + jmethodID errorMsgMethodId = 0; + jstring errorJstring = 0; + const char *keyPtr = NULL; + void *pickledPtr = NULL; + OlmOutboundGroupSession* sessionPtr = NULL; + + LOGD("## outbound group session serializeDataWithKeyJni(): IN"); + + if(NULL == (sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz))) + { + LOGE(" ## serializeDataWithKeyJni(): failure - invalid session ptr"); + } + else if(0 == aKey) + { + LOGE(" ## serializeDataWithKeyJni(): failure - invalid key"); + } + else if(0 == aErrorMsg) + { + LOGE(" ## serializeDataWithKeyJni(): failure - invalid error object"); + } + else if(0 == (errorMsgJClass = env->GetObjectClass(aErrorMsg))) + { + LOGE(" ## serializeDataWithKeyJni(): failure - unable to get error class"); + } + else if(0 == (errorMsgMethodId = env->GetMethodID(errorMsgJClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"))) + { + LOGE(" ## serializeDataWithKeyJni(): failure - unable to get error method ID"); + } + else if(NULL == (keyPtr = env->GetStringUTFChars(aKey, 0))) + { + LOGE(" ## serializeDataWithKeyJni(): failure - keyPtr JNI allocation OOM"); + } + else + { + size_t pickledLength = olm_pickle_outbound_group_session_length(sessionPtr); + size_t keyLength = (size_t)env->GetStringUTFLength(aKey); + LOGD(" ## serializeDataWithKeyJni(): pickledLength=%lu keyLength=%lu",pickledLength, keyLength); + LOGD(" ## serializeDataWithKeyJni(): key=%s",(char const *)keyPtr); + + if(NULL == (pickledPtr = (void*)malloc((pickledLength+1)*sizeof(uint8_t)))) + { + LOGE(" ## serializeDataWithKeyJni(): failure - pickledPtr buffer OOM"); + } + else + { + size_t result = olm_pickle_outbound_group_session(sessionPtr, + (void const *)keyPtr, + keyLength, + (void*)pickledPtr, + pickledLength); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_outbound_group_session_last_error(sessionPtr); + LOGE(" ## serializeDataWithKeyJni(): failure - olm_pickle_outbound_group_session() Msg=%s",errorMsgPtr); + + if(0 != (errorJstring = env->NewStringUTF(errorMsgPtr))) + { + env->CallObjectMethod(aErrorMsg, errorMsgMethodId, errorJstring); + } + } + else + { + // build success output + (static_cast(pickledPtr))[pickledLength] = static_cast('\0'); + pickledDataRetValue = env->NewStringUTF((const char*)pickledPtr); + LOGD(" ## serializeDataWithKeyJni(): success - result=%lu pickled=%s", result, static_cast(pickledPtr)); + } + } + } + + // free alloc + if(NULL != keyPtr) + { + env->ReleaseStringUTFChars(aKey, keyPtr); + } + + if(NULL != pickledPtr) + { + free(pickledPtr); + } + + return pickledDataRetValue; +} + + +JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(initWithSerializedDataJni)(JNIEnv *env, jobject thiz, jstring aSerializedData, jstring aKey) +{ + OlmOutboundGroupSession* sessionPtr = NULL; + jstring errorMessageRetValue = 0; + const char *keyPtr = NULL; + const char *pickledPtr = NULL; + + LOGD("## initWithSerializedDataJni(): IN"); + + if(NULL == (sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz))) + { + LOGE(" ## initWithSerializedDataJni(): failure - session failure OOM"); + } + else if(0 == aKey) + { + LOGE(" ## initWithSerializedDataJni(): failure - invalid key"); + } + else if(0 == aSerializedData) + { + LOGE(" ## initWithSerializedDataJni(): failure - serialized data"); + } + else if(NULL == (keyPtr = env->GetStringUTFChars(aKey, 0))) + { + LOGE(" ## initWithSerializedDataJni(): failure - keyPtr JNI allocation OOM"); + } + else if(NULL == (pickledPtr = env->GetStringUTFChars(aSerializedData, 0))) + { + LOGE(" ## initWithSerializedDataJni(): failure - pickledPtr JNI allocation OOM"); + } + else + { + size_t pickledLength = (size_t)env->GetStringUTFLength(aSerializedData); + size_t keyLength = (size_t)env->GetStringUTFLength(aKey); + LOGD(" ## initWithSerializedDataJni(): pickledLength=%lu keyLength=%lu",pickledLength, keyLength); + LOGD(" ## initWithSerializedDataJni(): key=%s",(char const *)keyPtr); + LOGD(" ## initWithSerializedDataJni(): pickled=%s",(char const *)pickledPtr); + + size_t result = olm_unpickle_outbound_group_session(sessionPtr, + (void const *)keyPtr, + keyLength, + (void*)pickledPtr, + pickledLength); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_outbound_group_session_last_error(sessionPtr); + LOGE(" ## initWithSerializedDataJni(): failure - olm_unpickle_outbound_group_session() Msg=%s",errorMsgPtr); + errorMessageRetValue = env->NewStringUTF(errorMsgPtr); + } + else + { + LOGD(" ## initWithSerializedDataJni(): success - result=%lu ", result); + } + } + + // free alloc + if(NULL != keyPtr) + { + env->ReleaseStringUTFChars(aKey, keyPtr); + } + + if(NULL != pickledPtr) + { + env->ReleaseStringUTFChars(aSerializedData, pickledPtr); + } + + return errorMessageRetValue; +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h index 6e264aa..22c32a7 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h @@ -38,6 +38,9 @@ JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionKeyJni)(JNIEnv *env JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jstring aClearMsgPtr); +// serialization +JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(serializeDataWithKeyJni)(JNIEnv *env, jobject thiz, jstring aKey, jobject aErrorMsg); +JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(initWithSerializedDataJni)(JNIEnv *env, jobject thiz, jstring aSerializedData, jstring aKey); #ifdef __cplusplus } diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp index 3422fee..9f64d2e 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp @@ -41,7 +41,6 @@ OlmSession* initializeSessionMemory() return sessionPtr; } - JNIEXPORT jlong OLM_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz) { LOGD("## createNewSessionJni(): IN"); -- cgit v1.2.3