From 5573d3ab23de21b93b1ecc50d4fce96b02a42886 Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Wed, 5 Oct 2016 18:25:09 +0200 Subject: First commit adding Olm Lib for Android - Add Android Studio project --- .../OlmLibSdk/olm-sdk/src/main/jni/Android.mk | 52 +++ .../OlmLibSdk/olm-sdk/src/main/jni/Application.mk | 3 + .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp | 491 +++++++++++++++++++++ .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.h | 34 ++ .../OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h | 46 ++ .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp | 476 ++++++++++++++++++++ .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.h | 38 ++ 7 files changed, 1140 insertions(+) create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h (limited to 'java/android/OlmLibSdk/olm-sdk/src/main/jni') diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk new file mode 100644 index 0000000..d59f916 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk @@ -0,0 +1,52 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := olm +MAJOR := 1 +MINOR := 3 +PATCH := 0 +OLM_VERSION := $(MAJOR).$(MINOR).$(PATCH) +SRC_ROOT_DIR := ../../../../../../.. + +$(info LOCAL_PATH=$(LOCAL_PATH)) +$(info SRC_ROOT_DIR=$(SRC_ROOT_DIR)) +$(info OLM_VERSION=$(OLM_VERSION)) + +LOCAL_CPPFLAGS+= -std=c++11 -Wall +LOCAL_CONLYFLAGS+= -std=c99 +LOCAL_CFLAGS+= -DOLMLIB_VERSION_MAJOR=$(MAJOR) \ +-DOLMLIB_VERSION_MINOR=$(MINOR) \ +-DOLMLIB_VERSION_PATCH=$(PATCH) + +LOCAL_C_INCLUDES+= $(LOCAL_PATH)/$(SRC_ROOT_DIR)/include/ \ +$(LOCAL_PATH)/$(SRC_ROOT_DIR)/lib + +$(info LOCAL_C_INCLUDES=$(LOCAL_C_INCLUDES)) + +LOCAL_SRC_FILES := $(SRC_ROOT_DIR)/src/account.cpp \ +$(SRC_ROOT_DIR)/src/base64.cpp \ +$(SRC_ROOT_DIR)/src/cipher.cpp \ +$(SRC_ROOT_DIR)/src/crypto.cpp \ +$(SRC_ROOT_DIR)/src/memory.cpp \ +$(SRC_ROOT_DIR)/src/message.cpp \ +$(SRC_ROOT_DIR)/src/olm.cpp \ +$(SRC_ROOT_DIR)/src/pickle.cpp \ +$(SRC_ROOT_DIR)/src/ratchet.cpp \ +$(SRC_ROOT_DIR)/src/session.cpp \ +$(SRC_ROOT_DIR)/src/utility.cpp \ +$(SRC_ROOT_DIR)/src/ed25519.c \ +$(SRC_ROOT_DIR)/src/error.c \ +$(SRC_ROOT_DIR)/src/inbound_group_session.c \ +$(SRC_ROOT_DIR)/src/megolm.c \ +$(SRC_ROOT_DIR)/src/outbound_group_session.c \ +$(SRC_ROOT_DIR)/src/pickle_encoding.c \ +$(SRC_ROOT_DIR)/lib/crypto-algorithms/sha256.c \ +$(SRC_ROOT_DIR)/lib/crypto-algorithms/aes.c \ +$(SRC_ROOT_DIR)/lib/curve25519-donna/curve25519-donna.c \ +olm_account.cpp \ +olm_session.cpp + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) + diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk new file mode 100644 index 0000000..29a4296 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk @@ -0,0 +1,3 @@ +APP_PLATFORM := android-21 +APP_ABI := arm64-v8a #armeabi-v7a armeabi x86 x86_64 +APP_STL := gnustl_static \ No newline at end of file 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 new file mode 100644 index 0000000..d8ee409 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp @@ -0,0 +1,491 @@ +/* + * 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. + */ + +#include "olm_account.h" + + +/** +* Init memory allocation for account creation. +* @return valid memory alocation, NULL otherwise +**/ +OlmAccount* initializeAccountMemory() +{ + OlmAccount* accountPtr = NULL; + size_t accountSize = olm_account_size(); + + if(NULL != (accountPtr=(OlmAccount*)malloc(accountSize))) + { // init account object + accountPtr = olm_account(accountPtr); + LOGD("## initializeAccountMemory(): success - OLM account size=%lu",accountSize); + } + else + { + LOGE("## initializeAccountMemory(): failure - OOM"); + } + + return accountPtr; +} + +/** + * Release the account allocation made by initializeAccountMemory().
+ * This method MUST be called when java counter part account instance is done. + * + */ +JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv *env, jobject thiz) +{ + OlmAccount* accountPtr = NULL; + + LOGD("## releaseAccountJni(): accountPtr=%p",accountPtr); + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## releaseAccountJni(): failure - invalid Account ptr=NULL"); + } + else + { // even if free(NULL) does not crash, a test is performed for debug purpose + LOGD("## releaseAccountJni(): IN"); + free(accountPtr); + LOGD("## releaseAccountJni(): OUT"); + } +} + +/** +* Initialize a new account and return it to JAVA side.
+* Since a C prt is returned as a jlong, special care will be taken +* to make the cast (OlmAccount* => jlong) platform independant. +* @return the initialized OlmAccount* instance if init succeed, NULL otherwise +**/ +JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv *env, jobject thiz) +{ + OlmAccount* accountPtr = NULL; + size_t accountRetCode; + uint8_t* randomBuffPtr = NULL; + int randomSize; + + // init account memory allocation + if(NULL == (accountPtr = initializeAccountMemory())) + { + LOGE("## initNewAccount(): failure - init account OOM"); + } + else + { + // allocate random buffer + randomSize = olm_create_account_random_length(accountPtr); + if(NULL == (randomBuffPtr = (std::uint8_t*)malloc(randomSize*sizeof(std::uint8_t)))) + { + LOGE("## initNewAccount(): failure - random buffer OOM"); + } + else + { // create random buffer + LOGD("## initNewAccount(): randomSize=%d",randomSize); + + srand(time(NULL)); // init seed + for(int i=0;i +* The keys are returned in the byte array. +* @return a valid byte array if operation succeed, null otherwise +**/ +JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_identityKeysJni(JNIEnv *env, jobject thiz) +{ + OlmAccount* accountPtr = NULL; + size_t identityKeysLength; + uint8_t *identityKeysBytesPtr; + size_t keysResult; + jbyteArray byteArrayRetValue = NULL; + + LOGD("## identityKeys(): accountPtr =%p",accountPtr); + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## identityKeys(): failure - invalid Account ptr=NULL"); + } + else + { // identity keys allocation + identityKeysLength = olm_account_identity_keys_length(accountPtr); + if(NULL == (identityKeysBytesPtr=(uint8_t *)malloc(identityKeysLength*sizeof(std::uint8_t)))) + { + LOGE("## identityKeys(): failure - identity keys array OOM"); + } + else + { // retrieve key pairs in identityKeysBytesPtr + keysResult = olm_account_identity_keys(accountPtr, identityKeysBytesPtr, identityKeysLength); + if(keysResult == olm_error()) { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGE("## identityKeys(): failure - error getting identity keys Msg=%s",errorMsgPtr); + } + else + { // allocate the byte array to be returned to java + if(NULL == (byteArrayRetValue=env->NewByteArray(identityKeysLength))) + { + LOGE("## identityKeys(): failure - return byte array OOM"); + } + else + { + env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, identityKeysLength, (const jbyte*)identityKeysBytesPtr); + LOGD("## identityKeys(): success - result=%ld", keysResult); + } + } + + free(identityKeysBytesPtr); + } + } + + return byteArrayRetValue; +} + +// ********************************************************************* +// ************************* ONE TIME KEYS API ************************* +// ********************************************************************* +/** + * Get the maximum number of "one time keys" the account can store. + * +**/ +JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_maxOneTimeKeys(JNIEnv *env, jobject thiz) +{ + OlmAccount* accountPtr = NULL; + size_t maxKeys = -1; + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## maxOneTimeKey(): failure - invalid Account ptr=NULL"); + } + else + { + maxKeys = olm_account_max_number_of_one_time_keys(accountPtr); + } + LOGD("## maxOneTimeKey(): Max keys=%ld", maxKeys); + + return (jlong)maxKeys; +} + +/** + * Generate "one time keys". + * @param aNumberOfKeys number of keys to generate + * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise +**/ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv *env, jobject thiz, jint aNumberOfKeys) +{ + OlmAccount* accountPtr = NULL;; + jint retCode = ERROR_CODE_KO; + size_t length; + void* keysBytesPtr; // TODO check type: or uint8_t? + size_t result; + + LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys); + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## generateOneTimeKeys(): failure - invalid Account ptr"); + } + else + { // keys memory allocation + length = olm_account_generate_one_time_keys_random_length(accountPtr, aNumberOfKeys); + LOGD("## generateOneTimeKeys(): randomLength=%ld", length); + if(NULL == (keysBytesPtr=(void*)malloc(length*sizeof(void*)))) + { + LOGE("## generateOneTimeKeys(): failure - random allocation OOM"); + } + else + { // retrieve key pairs in keysBytesPtr + result = olm_account_generate_one_time_keys(accountPtr, aNumberOfKeys, keysBytesPtr, length); + if(result == olm_error()) { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGE("## generateOneTimeKeys(): failure - error generating one time keys Msg=%s",errorMsgPtr); + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## generateOneTimeKeys(): success - result=%ld", result); + } + + free(keysBytesPtr); + } + } + + return retCode; +} + +/** + * Get "one time keys". + * Return the public parts of the unpublished "one time keys" for the account + * @return a valid byte array if operation succeed, null otherwise +**/ +JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_oneTimeKeysJni(JNIEnv *env, jobject thiz) +{ + OlmAccount* accountPtr = NULL; + size_t keysLength; + uint8_t *keysBytesPtr; + size_t keysResult; + jbyteArray byteArrayRetValue = NULL; + + LOGD("## oneTimeKeys(): accountPtr =%p",accountPtr); + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## oneTimeKeys(): failure - invalid Account ptr"); + } + else + { // keys memory allocation + keysLength = olm_account_one_time_keys_length(accountPtr); + if(NULL == (keysBytesPtr=(uint8_t *)malloc(keysLength*sizeof(uint8_t)))) + { + LOGE("## oneTimeKeys(): failure - one time keys array OOM"); + } + else + { // retrieve key pairs in keysBytesPtr + keysResult = olm_account_one_time_keys(accountPtr, keysBytesPtr, keysLength); + if(keysResult == olm_error()) { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGE("## oneTimeKeys(): failure - error getting one time keys Msg=%s",errorMsgPtr); + } + else + { // allocate the byte array to be returned to java + if(NULL == (byteArrayRetValue=env->NewByteArray(keysLength))) + { + LOGE("## oneTimeKeys(): failure - return byte array OOM"); + } + else + { + env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, keysLength, (const jbyte*)keysBytesPtr); + LOGD("## oneTimeKeys(): success"); + } + } + + free(keysBytesPtr); + } + } + + return byteArrayRetValue; +} + +/** + * Remove the "one time keys" that the session used from the account. + * Return the public parts of the unpublished "one time keys" for the account + * @param aNativeOlmSessionId session instance + * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS if no matching keys, ERROR_CODE_KO otherwise +**/ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_removeOneTimeKeysForSession(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId) +{ + jint retCode = ERROR_CODE_KO; + OlmAccount* accountPtr = NULL; + OlmSession* sessionPtr = (OlmSession*)aNativeOlmSessionId; + size_t result; + + if(NULL == sessionPtr) + { + LOGE("## removeOneTimeKeysForSession(): failure - invalid session ptr"); + } + else if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## removeOneTimeKeysForSession(): failure - invalid account ptr"); + } + else + { + result = olm_remove_one_time_keys(accountPtr, sessionPtr); + if(result == olm_error()) + { // the account doesn't have any matching "one time keys".. + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGW("## removeOneTimeKeysForSession(): failure - removing one time keys Msg=%s",errorMsgPtr); + + retCode = ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS; + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## removeOneTimeKeysForSession(): success"); + } + } + + return retCode; +} + +/** + * Mark the current set of "one time keys" as being published. + * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise +**/ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_markOneTimeKeysAsPublished(JNIEnv *env, jobject thiz) +{ + jint retCode = ERROR_CODE_OK; + OlmAccount* accountPtr = NULL; + size_t result; + + if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## markOneTimeKeysPublished(): failure - invalid account ptr"); + retCode = ERROR_CODE_KO; + } + else + { + result = olm_account_mark_keys_as_published(accountPtr); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGW("## markOneTimeKeysPublished(): failure - Msg=%s",errorMsgPtr); + retCode = ERROR_CODE_KO; + } + else + { + LOGD("## markOneTimeKeysPublished(): success - retCode=%ld",result); + } + } + + return retCode; +} + +/** + * Sign a message with the ed25519 key (fingerprint) for this account. + * @param aMessage message to sign + * @return the corresponding signed message, null otherwise +**/ +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env, jobject thiz, jstring aMessage) +{ + OlmAccount* accountPtr = NULL; + size_t signatureLength; + void* signaturePtr; + size_t resultSign; + jstring signedMsgRetValue = NULL; + + if(NULL == aMessage) + { + LOGE("## signMessage(): failure - invalid aMessage param"); + } + else if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) + { + LOGE("## signMessage(): failure - invalid account ptr"); + } + else + { + // convert message from JAVA to C string + const char* messageToSign = env->GetStringUTFChars(aMessage, 0); + if(NULL == messageToSign) + { + LOGE("## signMessage(): failure - message JNI allocation OOM"); + } + else + { + int messageLength = env->GetStringUTFLength(aMessage); + + // signature memory allocation + signatureLength = olm_account_signature_length(accountPtr); + if(NULL == (signaturePtr=(void *)malloc(signatureLength*sizeof(void*)))) + { + LOGE("## signMessage(): failure - signature allocation OOM"); + } + else + { // sign message + resultSign = olm_account_sign(accountPtr, (void*)messageToSign, messageLength, signaturePtr, signatureLength); + if(resultSign == olm_error()) + { + const char *errorMsgPtr = olm_account_last_error(accountPtr); + LOGE("## signMessage(): failure - error signing message Msg=%s",errorMsgPtr); + } + else + { // convert to jstring + // TODO check how UTF conversion can impact the content? + // why not consider return jbyteArray? and convert in JAVA side.. + signedMsgRetValue = env->NewStringUTF((const char*)signaturePtr); // UTF8 + LOGD("## signMessage(): success - retCode=%ld",resultSign); + } + + free(signaturePtr); + } + + // release messageToSign + env->ReleaseStringUTFChars(aMessage, messageToSign); + } + } + + return signedMsgRetValue; +} + + +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmManager_getOlmLibVersion(JNIEnv* env, jobject thiz) +{ + uint8_t majorVer=0, minorVer=0, patchVer=0; + jstring returnValueStr=0; + char buff[150]; + + olm_get_library_version(&majorVer, &minorVer, &patchVer); + LOGD("## getOlmLibVersion(): Major=%d Minor=%d Patch=%d", majorVer, minorVer, patchVer); + + snprintf(buff, sizeof(buff), " V%d.%d.%d", majorVer, minorVer, patchVer); + returnValueStr = env->NewStringUTF((const char*)buff); + + return returnValueStr; +} + + +/** +* Read the account instance ID of the calling object. +* @return the instance ID if read succeed, -1 otherwise. +**/ +jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject) +{ + jlong instanceId=-1; + jfieldID instanceIdField; + jclass loaderClass; + + if(NULL!=aJniEnv) + { + if(0 != (loaderClass=aJniEnv->GetObjectClass(aJavaObject))) + { + if(0 != (instanceIdField=aJniEnv->GetFieldID(loaderClass, "mNativeOlmAccountId", "J"))) + { + instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField); + aJniEnv->DeleteLocalRef(loaderClass); + LOGD("## getAccountInstanceId(): read from java instanceId=%lld",instanceId); + } + else + { + LOGD("## getAccountInstanceId() ERROR! GetFieldID=null"); + } + } + else + { + LOGD("## getAccountInstanceId() ERROR! GetObjectClass=null"); + } + } + else + { + LOGD("## getAccountInstanceId() ERROR! aJniEnv=NULL"); + } + LOGD("## getAccountInstanceId() success - instanceId=%lld",instanceId); + return instanceId; +} 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 new file mode 100644 index 0000000..8ba1633 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h @@ -0,0 +1,34 @@ +#ifndef _OMLACCOUNT_H +#define _OMLACCOUNT_H + +#include "olm_jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject); +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmManager_getOlmLibVersion(JNIEnv *env, jobject thiz); + +// account creation/destruction +JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv *env, jobject thiz); +JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv *env, jobject thiz); + +// identity keys +JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_identityKeysJni(JNIEnv *env, jobject thiz); + +// one time keys +JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_oneTimeKeysJni(JNIEnv *env, jobject thiz); +JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_maxOneTimeKeys(JNIEnv *env, jobject thiz); +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv *env, jobject thiz, jint aNumberOfKeys); +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_removeOneTimeKeysForSession(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId); +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_markOneTimeKeysAsPublished(JNIEnv *env, jobject thiz); + +// signing +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env, jobject thiz, jstring aMessage); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h new file mode 100644 index 0000000..a504333 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h @@ -0,0 +1,46 @@ +#ifndef _OMLJNI_H +#define _OMLJNI_H + +#include +#include +#include +#include +#include +#include +#include + +#include "olm/olm.h" + +#define TAG "OlmJniNative" + +/* logging macros */ +#define ENABLE_LOGS + +#ifdef ENABLE_LOGS + #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__) + #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) + #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) + #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) +#else + #define LOGV(...) + #define LOGD(...) + #define LOGW(...) + #define LOGE(...) +#endif + +// Error codes definition +static const int ERROR_CODE_OK = 0; +static const int ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS = ERROR_CODE_OK+1; +static const int ERROR_CODE_KO = -1; + +// constants +static const int ACCOUNT_CREATION_RANDOM_MODULO = 500; + + +typedef struct _AccountContext +{ + OlmAccount* mAccountPtr; + _AccountContext(): mAccountPtr(NULL){} +} AccountContext; + +#endif 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 new file mode 100644 index 0000000..22544d4 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp @@ -0,0 +1,476 @@ +/* + * 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. + */ + +#include "olm_session.h" + + +/** +* Init memory allocation for session creation. +* @return valid memory allocation, NULL otherwise +**/ +OlmSession* initializeSessionMemory() +{ + OlmSession* sessionPtr = NULL; + size_t sessionSize = olm_session_size(); + + if(NULL != (sessionPtr=(OlmSession*)malloc(sessionSize))) + { // init session object + sessionPtr = olm_session(sessionPtr); + LOGD("## initializeSessionMemory(): success - OLM session size=%lu",sessionSize); + } + else + { + LOGE("## initializeSessionMemory(): failure - OOM"); + } + + return sessionPtr; +} + +JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv *env, jobject thiz) +{ + OlmSession* sessionPtr = NULL; + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL"); + } + else + { // even if free(NULL) does not crash, a test is performed for debug purpose + LOGD("## releaseSessionJni(): IN"); + free(sessionPtr); + LOGD("## releaseSessionJni(): OUT"); + } +} + +/** +* Initialize a new session and return it to JAVA side.
+* Since a C prt is returned as a jlong, special care will be taken +* to make the cast (OlmSession* => jlong) platform independent. +* @return the initialized OlmSession* instance if init succeed, NULL otherwise +**/ +JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmSession_initNewSessionJni(JNIEnv *env, jobject thiz) +{ + OlmSession* sessionPtr = NULL; + + // init account memory allocation + if(NULL == (sessionPtr = initializeSessionMemory())) + { + LOGE("## initNewSessionJni(): failure - init session OOM"); + } + else + { + LOGD("## initNewSessionJni(): success - OLM session created"); + } + + return (jlong)(intptr_t)sessionPtr; +} + +// ********************************************************************* +// ********************** OUTBOUND SESSION ***************************** +// ********************************************************************* +/** +* Create a new in-bound session for sending/receiving messages from an +* incoming PRE_KEY message.
The recipient is defined as the entity +* with whom the session is established. +* @param aOlmAccountId account instance +* @param aTheirIdentityKey the identity key of the recipient +* @param aTheirOneTimeKey the one time key of the recipient +* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise +**/ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey) +{ + jint retCode = ERROR_CODE_KO; + OlmSession* sessionPtr = NULL; + OlmAccount* accountPtr = NULL; + void *randomBuffPtr; + size_t sessionResult; + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## initOutboundSessionJni(): failure - invalid Session ptr=NULL"); + } + else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId)) + { + LOGE("## initOutboundSessionJni(): failure - invalid Account ptr=NULL"); + } + else if((0==aTheirIdentityKey) || (0==aTheirOneTimeKey)) + { + LOGE("## initOutboundSessionJni(): failure - invalid keys"); + } + else + { // allocate random buffer + size_t randomSize = olm_create_outbound_session_random_length(sessionPtr); + if(NULL == (randomBuffPtr = (void*)malloc(randomSize*sizeof(void*)))) + { + LOGE("## initOutboundSessionJni(): failure - random buffer OOM"); + } + else + { // convert identity & one time keys to C strings + const char* theirIdentityKeyPtr = NULL; + const char* theirOneTimeKeyPtr = NULL; + + if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0))) + { + LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM"); + } + else if(NULL == (theirOneTimeKeyPtr = env->GetStringUTFChars(aTheirOneTimeKey, 0))) + { + LOGE("## initOutboundSessionJni(): failure - one time Key JNI allocation OOM"); + } + else + { + int theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey); + int theirOneTimeKeyLength = env->GetStringUTFLength(aTheirOneTimeKey); + LOGD("## initOutboundSessionJni(): identityKey=%s oneTimeKey=%s",theirIdentityKeyPtr,theirOneTimeKeyPtr); + + sessionResult = olm_create_outbound_session(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, theirOneTimeKeyPtr, theirOneTimeKeyLength, randomBuffPtr, randomSize); + if(sessionResult == olm_error()) { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## initOutboundSessionJni(): failure - session creation Msg=%s",errorMsgPtr); + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## initOutboundSessionJni(): success - result=%ld", sessionResult); + } + } + + // free local alloc + free(randomBuffPtr); + if(NULL!= theirIdentityKeyPtr) + { + env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr); + } + if(NULL!= theirOneTimeKeyPtr) + { + env->ReleaseStringUTFChars(aTheirOneTimeKey, theirOneTimeKeyPtr); + } + } + } + + return retCode; +} + + +// ********************************************************************* +// *********************** INBOUND SESSION ***************************** +// ********************************************************************* +/** + * Create a new in-bound session for sending/receiving messages from an + * incoming PRE_KEY message.
+ * @param aOlmAccountId account instance + * @param aOneTimeKeyMsg PRE_KEY message TODO TBC + * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise + */ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg) +{ + jint retCode = ERROR_CODE_KO; + OlmSession *sessionPtr = NULL; + OlmAccount *accountPtr = NULL; + size_t sessionResult; + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## initInboundSessionJni(): failure - invalid Session ptr=NULL"); + } + else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId)) + { + LOGE("## initInboundSessionJni(): failure - invalid Account ptr=NULL"); + } + else if(0==aOneTimeKeyMsg) + { + LOGE("## initOutboundSessionJni(): failure - invalid message"); + } + else + { // convert message to C strings + const char *messagePtr = NULL; + if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0))) + { + LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM"); + } + else + { + int messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + LOGD("## initInboundSessionJni(): message=%s messageLength=%d",messagePtr,messageLength); + + sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength); + if(sessionResult == olm_error()) { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## initInboundSessionJni(): failure - init inbound session creation Msg=%s",errorMsgPtr); + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## initInboundSessionJni(): success - result=%ld", sessionResult); + } + + // free local alloc + env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr); + } + } + return retCode; +} + +/** + * Create a new in-bound session for sending/receiving messages from an + * incoming PRE_KEY message based on the recipient identity key.
+ * @param aOlmAccountId account instance + * @param aTheirIdentityKey the identity key of the recipient + * @param aOneTimeKeyMsg encrypted message + * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise + */ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg) +{ + jint retCode = ERROR_CODE_KO; + OlmSession *sessionPtr = NULL; + OlmAccount *accountPtr = NULL; + const char *messagePtr = NULL; + const char *theirIdentityKeyPtr = NULL; + size_t sessionResult; + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL"); + } + else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId)) + { + LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Account ptr=NULL"); + } + else if(0 == aTheirIdentityKey) + { + LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey"); + } + else if(0==aOneTimeKeyMsg) + { + LOGE("## initOutboundSessionJni(): failure - invalid one time key message"); + } + else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0))) + { + LOGE("## initInboundSessionFromIdKeyJni(): failure - message JNI allocation OOM"); + } + else if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0))) + { + LOGE("## initInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM"); + } + else + { + size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + size_t theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey); + + LOGD("## initInboundSessionFromIdKeyJni(): message=%s messageLength=%lu",messagePtr,messageLength); + + sessionResult = olm_create_inbound_session_from(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, (void*)messagePtr , messageLength); + if(sessionResult == olm_error()) { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation Msg=%s",errorMsgPtr); + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## initInboundSessionFromIdKeyJni(): success - result=%ld", sessionResult); + } + } + + // free local alloc + if(NULL!= messagePtr) + { + env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr); + } + if(NULL!= theirIdentityKeyPtr) + { + env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr); + } + + return retCode; +} + +/** + * Checks if the PRE_KEY message is for this in-bound session.
+ * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). + * @param aOneTimeKeyMsg PRE KEY message + * @return ERROR_CODE_OK if match, ERROR_CODE_KO otherwise + */ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(JNIEnv *env, jobject thiz, jstring aOneTimeKeyMsg) +{ + jint retCode = ERROR_CODE_KO; + OlmSession *sessionPtr = NULL; + const char *messagePtr = NULL; + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## matchesInboundSessionJni(): failure - invalid Session ptr=NULL"); + } + else if(0==aOneTimeKeyMsg) + { + LOGE("## matchesInboundSessionJni(): failure - invalid one time key message"); + } + else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0))) + { + LOGE("## matchesInboundSessionJni(): failure - one time key JNI allocation OOM"); + } + else + { + size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + + size_t matchResult = olm_matches_inbound_session(sessionPtr, (void*)messagePtr , messageLength); + if(matchResult == olm_error()) { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## matchesInboundSessionJni(): failure - no match Msg=%s",errorMsgPtr); + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## matchesInboundSessionJni(): success - result=%ld", matchResult); + } + } + + return retCode; +} + + +/** + * Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.
+ * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). + * @param aTheirIdentityKey the identity key of the sender + * @param aOneTimeKeyMsg PRE KEY message + * @return ERROR_CODE_OK if match, ERROR_CODE_KO otherwise + */ +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg) +{ + jint retCode = ERROR_CODE_KO; + OlmSession *sessionPtr = NULL; + const char *messagePtr = NULL; + const char *theirIdentityKeyPtr = NULL; + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL"); + } + else if(0 == aTheirIdentityKey) + { + LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey"); + } + else if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0))) + { + LOGE("## matchesInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM"); + } + else if(0==aOneTimeKeyMsg) + { + LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid one time key message"); + } + else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0))) + { + LOGE("## matchesInboundSessionFromIdKeyJni(): failure - one time key JNI allocation OOM"); + } + else + { + size_t identityKeyLength = env->GetStringUTFLength(aTheirIdentityKey); + size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + + size_t matchResult = olm_matches_inbound_session_from(sessionPtr, (void const *)theirIdentityKeyPtr, identityKeyLength, (void*)messagePtr , messageLength); + if(matchResult == olm_error()) { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## matchesInboundSessionFromIdKeyJni(): failure - no match Msg=%s",errorMsgPtr); + } + else + { + retCode = ERROR_CODE_OK; + LOGD("## matchesInboundSessionFromIdKeyJni(): success - result=%lu", matchResult); + } + } + + return retCode; +} + + +/** +* Get the session identifier for this session. +* @return the session identifier if operation succeed, null otherwise +*/ +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz) +{ + OlmSession *sessionPtr = NULL; + void *sessionIdPtr = NULL; + jstring returnValueStr=0; + + // get the size to alloc to contain the id + size_t lengthSessId = olm_session_id_length(sessionPtr); + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## getSessionIdentifierJni(): failure - invalid Session ptr=NULL"); + } + else if(NULL == (sessionIdPtr = (void*)malloc(lengthSessId*sizeof(void*)))) + { + LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM"); + } + else + { + size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessId); + if (result == olm_error()) + { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr); + } + else + { + returnValueStr = env->NewStringUTF((const char*)sessionIdPtr); + } + free(sessionIdPtr); + } + + return returnValueStr; +} + +/** +* Read the account instance ID of the calling object (aJavaObject) passed in parameter. +* @param aJniEnv pointer pointing on the JNI function table +* @param aJavaObject reference to the object on which the method is invoked +* @return the instance ID if read succeed, -1 otherwise. +**/ +jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject) +{ + jlong instanceId=-1; + jfieldID instanceIdField; + jclass loaderClass; + + if(NULL!=aJniEnv) + { + if(0 != (loaderClass=aJniEnv->GetObjectClass(aJavaObject))) + { + if(0 != (instanceIdField=aJniEnv->GetFieldID(loaderClass, "mNativeOlmSessionId", "J"))) + { + instanceId = aJniEnv->GetIntField(aJavaObject, instanceIdField); + aJniEnv->DeleteLocalRef(loaderClass); + } + else + { + LOGD("## getSessionInstanceId() ERROR! GetFieldID=null"); + } + } + else + { + LOGD("## getSessionInstanceId() ERROR! GetObjectClass=null"); + } + } + else + { + LOGD("## getSessionInstanceId() ERROR! aJniEnv=NULL"); + } + + LOGD("## getSessionInstanceId() success - instanceId=%lld",instanceId); + return instanceId; +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h new file mode 100644 index 0000000..edd1012 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h @@ -0,0 +1,38 @@ +#ifndef _OMLSESSION_H +#define _OMLSESSION_H + +#include "olm_jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject); + +// session creation/destruction +JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv *env, jobject thiz); +JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmSession_initNewSessionJni(JNIEnv *env, jobject thiz); + +// outbound session +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey); + +// inbound sessions: establishment based on PRE KEY message +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg); +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg); + +// match inbound sessions: based on PRE KEY message +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(JNIEnv *env, jobject thiz, jstring aOneTimeKeyMsg); +JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg); + +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz); + + +// signing + + + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3