From 1511962df194b9b414b3629b37b6fd3a42081295 Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Thu, 20 Oct 2016 17:42:57 +0200 Subject: Add serialization feature to OlmAccount - new JNI API: serializeDataWithKeyJni() and initWithSerializedDataJni() - update account unit test - modify OlmAccount constructor API: an exception may be thrown --- .../src/main/java/org/matrix/olm/OlmAccount.java | 72 ++++---- .../src/main/java/org/matrix/olm/OlmException.java | 2 + .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp | 183 ++++++++++++++++++++- .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.h | 5 + 4 files changed, 230 insertions(+), 32 deletions(-) (limited to 'java/android/OlmLibSdk/olm-sdk/src/main') diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java index 5b2418a..61b6c52 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java @@ -26,17 +26,13 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.Arrays; import java.util.Random; public class OlmAccount implements Serializable { private static final long serialVersionUID = 3497486121598434824L; private static final String LOG_TAG = "OlmAccount"; - private static final int MAX_BITS_LENGTH = 128; - private static final int RANDOM_TAB_SIZE = 32; - private static final int RANDOM_MAX = 256; + private static final int RANDOM_KEY_SIZE = 32; + private static final int RANDOM_RANGE = 256; // JSON keys used in the JSON objects returned by JNI /** As well as the identity key, each device creates a number of Curve25519 key pairs which are @@ -60,24 +56,22 @@ public class OlmAccount implements Serializable { */ private transient long mNativeOlmAccountId; - private transient SecureRandom mSecureRandom; - public OlmAccount() { - initNewAccount(); - mSecureRandom = new SecureRandom(); + public OlmAccount() throws OlmException { + if(!initNewAccount()) { + throw new OlmException(OlmException.EXCEPTION_CODE_INIT_ACCOUNT_CREATION,OlmException.EXCEPTION_MSG_INIT_ACCOUNT_CREATION); + } } private String getRandomKey() { - //String keyRetValue = new BigInteger(MAX_BITS_LENGTH, mSecureRandom).toString(RANDOM_TAB_SIZE); String keyRetValue; Random rand = new Random(); - StringBuilder strBuilder = new StringBuilder(); - for(int i=0;i + * Public API for {@link #releaseAccountJni()}. + */ + public void releaseAccount(){ + releaseAccountJni(); + + mNativeOlmAccountId = 0; + } + /** * Destroy the corresponding OLM account native object.
* This method must ALWAYS be called when this JAVA instance @@ -199,36 +203,48 @@ public class OlmAccount implements Serializable { private native void releaseAccountJni(); /** - * Release native account and invalid its JAVA reference counter part.
- * Public API for {@link #releaseAccountJni()}. + * Create and initialize a native account instance.
+ * Wrapper for {@link #initNewAccountJni()}. + * To be called before any other API call. + * @return true if init succeed, false otherwise. */ - public void releaseAccount(){ - releaseAccountJni(); - - mNativeOlmAccountId = 0; + private boolean initNewAccount() { + boolean retCode = false; + if(0 != (mNativeOlmAccountId = initNewAccountJni())){ + retCode = true; + } + return retCode; } /** - * Create the corresponding OLM account in native side.
+ * Create and initialize an OLM account in native side.
* Do not forget to call {@link #releaseAccount()} when JAVA side is done. * @return native account instance identifier (see {@link #mNativeOlmAccountId}) */ private native long initNewAccountJni(); /** - * Create and initialize a new native account instance.
- * Wrapper for {@link #initNewAccountJni()}. - * To be called before any other API call. + * Create a native account instance without any initialization.
+ * Since the account is left uninitialized, this + * method is intended to be used in the serialization mechanism (see {@link #readObject(ObjectInputStream)}).
+ * Public wrapper for {@link #createNewAccountJni()}. * @return true if init succeed, false otherwise. */ - private boolean initNewAccount() { + private boolean createNewAccount() { boolean retCode = false; - if(0 != (mNativeOlmAccountId = initNewAccountJni())){ + if(0 != (mNativeOlmAccountId = createNewAccountJni())){ retCode = true; } return retCode; } + /** + * Create an OLM account in native side.
+ * Do not forget to call {@link #releaseAccount()} when JAVA side is done. + * @return native account instance identifier (see {@link #mNativeOlmAccountId}) + */ + private native long createNewAccountJni(); + /** * Return the identity keys (identity & fingerprint keys) in a JSON array.
* Public API for {@link #identityKeysJni()}.
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 97589f0..b83e77c 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 @@ -29,6 +29,7 @@ public class OlmException extends Exception { public static final int EXCEPTION_CODE_ACCOUNT_DESERIALIZATION = 5; public static final int EXCEPTION_CODE_SESSION_SERIALIZATION = 6; public static final int EXCEPTION_CODE_SESSION_DESERIALIZATION = 7; + public static final int EXCEPTION_CODE_INIT_ACCOUNT_CREATION = 8; // exception human readable messages public static final String EXCEPTION_MSG_NEW_OUTBOUND_GROUP_SESSION = "failed to create a new outbound group Session"; @@ -38,6 +39,7 @@ public class OlmException extends Exception { 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_INIT_ACCOUNT_CREATION = "Account constructor failure"; /** exception code to be taken from: {@link #EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION} {@link #EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION} * {@link #EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION} {@link #EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION}**/ 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 a10b8e0..6f2e6ee 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 @@ -40,6 +40,17 @@ OlmAccount* initializeAccountMemory() return accountPtr; } + +JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(createNewAccountJni)(JNIEnv *env, jobject thiz) +{ + LOGD("## createNewAccountJni(): IN"); + OlmAccount* accountPtr = initializeAccountMemory(); + + LOGD(" ## createNewAccountJni(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr); + return (jlong)(intptr_t)accountPtr; +} + + /** * Release the account allocation made by initializeAccountMemory().
* This method MUST be called when java counter part account instance is done. @@ -49,20 +60,21 @@ JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz { OlmAccount* accountPtr = NULL; - LOGD("## releaseAccountJni(): accountPtr=%p",accountPtr); + LOGD("## releaseAccountJni(): IN"); if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) { - LOGE("## releaseAccountJni(): failure - invalid Account ptr=NULL"); + LOGE(" ## releaseAccountJni(): failure - invalid Account ptr=NULL"); } else { + LOGD(" ## releaseAccountJni(): accountPtr=%p",accountPtr); olm_clear_account(accountPtr); - LOGD("## releaseAccountJni(): IN"); + LOGD(" ## releaseAccountJni(): IN"); // even if free(NULL) does not crash, logs are performed for debug purpose free(accountPtr); - LOGD("## releaseAccountJni(): OUT"); + LOGD(" ## releaseAccountJni(): OUT"); } } @@ -467,4 +479,167 @@ JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersion)(JNIEnv* env, jobject th return returnValueStr; } +/** +* Serialize and encrypt account instance into a base64 string.
+* @param aKey key used to encrypt the serialized account data +* @param[out] aErrorMsg error message set if operation failed +* @return a base64 string if operation succeed, null otherwise +**/ +JNIEXPORT jstring OLM_ACCOUNT_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; + OlmAccount* accountPtr = NULL; + + LOGD("## serializeDataWithKeyJni(): IN"); + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE(" ## serializeDataWithKeyJni(): failure - invalid account 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_account_length(accountPtr); + 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_account(accountPtr, + (void const *)keyPtr, + keyLength, + (void*)pickledPtr, + pickledLength); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGE(" ## serializeDataWithKeyJni(): failure - olm_pickle_account() 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_ACCOUNT_FUNC_DEF(initWithSerializedDataJni)(JNIEnv *env, jobject thiz, jstring aSerializedData, jstring aKey) +{ + OlmAccount* accountPtr = NULL; + jstring errorMessageRetValue = 0; + const char *keyPtr = NULL; + const char *pickledPtr = NULL; + + LOGD("## initWithSerializedDataJni(): IN"); + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + //if(NULL == (accountPtr = initializeAccountMemory())) + { + LOGE(" ## initWithSerializedDataJni(): failure - account 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_account(accountPtr, + (void const *)keyPtr, + keyLength, + (void*)pickledPtr, + pickledLength); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGE(" ## initWithSerializedDataJni(): failure - olm_unpickle_account() 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; +} \ No newline at end of file diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h index 63ec3ef..1c8fb96 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h @@ -32,6 +32,7 @@ JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersion)(JNIEnv *env, jobject th // account creation/destruction JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz); JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(initNewAccountJni)(JNIEnv *env, jobject thiz); +JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(createNewAccountJni)(JNIEnv *env, jobject thiz); // identity keys JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz); @@ -46,6 +47,10 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, // signing JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jstring aMessage); +// serialization +JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(serializeDataWithKeyJni)(JNIEnv *env, jobject thiz, jstring aKey, jobject aErrorMsg); +JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(initWithSerializedDataJni)(JNIEnv *env, jobject thiz, jstring aSerializedData, jstring aKey); + #ifdef __cplusplus } #endif -- cgit v1.2.3