From 3136826e02ee379858ff5a1c3aa9140f0136e813 Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Wed, 5 Oct 2016 15:31:52 +0200 Subject: [PATCH 1/9] First commit on depo - Add file --- .../main/java/org/matrix/olm/OlmAccount.java | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java 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 new file mode 100644 index 0000000..299a07d --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java @@ -0,0 +1,197 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.olm; + +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +public class OlmAccount { + private static final String LOG_TAG = "OlmAccount"; + + // JSON keys used in the JSON objects returned by JNI + public static String JSON_KEY_ONE_TIME_KEY = "curve25519"; + public static String JSON_KEY_FINGER_PRINT_KEY = "ed25519"; + + /** instance unique identifier, used in JNI to match the corresponding native class **/ + private int mJavaInstanceId; + + /** account raw pointer value (OlmAccount*) returned by JNI. + * this value identifies uniquely the native account instance. + */ + private long mNativeOlmAccountId; + + public OlmAccount() { + mJavaInstanceId = hashCode(); + //initNewAccount(); + } + + /** + * Getter on the account ID. + * @return native account ID + */ + public long getOlmAccountId(){ + return mNativeOlmAccountId; + } + + /** + * Destroy the corresponding OLM account native object.
+ * This method must ALWAYS be called when this JAVA instance + * is destroyed (ie. garbage collected) to prevent memory leak in native side. + * See {@link #initNewAccountJni()}. + */ + private native void releaseAccountJni(); + + /** + * Release native account and invalid its JAVA reference counter part.
+ * Public API for {@link #releaseAccountJni()}. + * To be called before any other API call. + */ + public void releaseAccount(){ + releaseAccountJni(); + + mNativeOlmAccountId = 0; + } + + /** + * Create the corresponding OLM account in native side.
+ * The return value is a long casted C ptr on the OlmAccount. + * 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 save the account native instance ID. + * Wrapper for {@link #initNewAccountJni()}.
+ * To be called before any other API call. + * @return true if init succeed, false otherwise. + */ + public boolean initNewAccount() { + boolean retCode = false; + if(0 != (mNativeOlmAccountId = initNewAccountJni())){ + retCode = true; + } + return retCode; + } + + /** + * Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).
+ * Keys are Base64 encoded. + * These keys must be published on the server. + * @return byte array containing the identity keys if operation succeed, null otherwise + */ + private native byte[] identityKeysJni(); + + /** + * Return the identity keys in a JSON array.
+ * Public API for {@link #identityKeysJni()}. + * @return identity keys in JSON array format if operation succeed, null otherwise + */ + public JSONObject identityKeys() { + JSONObject identityKeysJsonObj = null; + byte identityKeysBuffer[]; + + if( null != (identityKeysBuffer = identityKeysJni())) { + try { + identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer)); + Log.d(LOG_TAG, "## identityKeys(): Identity Json keys=" + identityKeysJsonObj.toString()); + } catch (JSONException e) { + identityKeysJsonObj = null; + Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage()); + } + } else { + Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null"); + } + + return identityKeysJsonObj; + } + + /** + * Return the largest number of "one time keys" this account can store. + * @return the max number of "one time keys", -1 otherwise + */ + public native long maxOneTimeKeys(); + + /** + * Generate a number of new one time keys.
If total number of keys stored + * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded. + * @param aNumberOfKeys number of keys to generate + * @return 0 if operation succeed, -1 otherwise + */ + public native int generateOneTimeKeys(int aNumberOfKeys); + + /** + * Get the public parts of the unpublished "one time keys" for the account.
+ * The returned data is a JSON-formatted object with the single property + * curve25519, which is itself an object mapping key id to + * base64-encoded Curve25519 key. + * These keys must be published on the server. + * @return byte array containing the one time keys if operation succeed, null otherwise + */ + private native byte[] oneTimeKeysJni(); + + /** + * Return the "one time keys" in a JSON array.
+ * Public API for {@link #oneTimeKeysJni()}. + * @return one time keys in JSON array format if operation succeed, null otherwise + */ + public JSONObject oneTimeKeys() { + byte identityKeysBuffer[]; + JSONObject identityKeysJsonObj = null; + + if( null != (identityKeysBuffer = oneTimeKeysJni())) { + try { + identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer)); + Log.d(LOG_TAG, "## oneTimeKeys(): Identity Json keys=" + identityKeysJsonObj.toString()); + } catch (JSONException e) { + identityKeysJsonObj = null; + Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage()); + } + } else { + Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null"); + } + + return identityKeysJsonObj; + } + + /** + * Remove the "one time keys" that the session used from the account. + * @param aNativeOlmSessionId native session instance identifier + * @return 0 if operation succeed, 1 if no matching keys in the sessions to be removed, -1 if operation failed + */ + public native int removeOneTimeKeysForSession(long aNativeOlmSessionId); + + /** + * Marks the current set of "one time keys" as being published. + * @return 0 if operation succeed, -1 otherwise + */ + public native int markOneTimeKeysAsPublished(); + + /** + * Sign a message with the ed25519 fingerprint key for this account. + * @param aMessage message to sign + * @return the signed message if operation succeed, null otherwise + */ + public native String signMessage(String aMessage); + + @Override + public String toString() { + return super.toString(); + } +} From 5573d3ab23de21b93b1ecc50d4fce96b02a42886 Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Wed, 5 Oct 2016 18:25:09 +0200 Subject: [PATCH 2/9] First commit adding Olm Lib for Android - Add Android Studio project --- java/android/OlmLibSdk/build.gradle | 23 + java/android/OlmLibSdk/gradle.properties | 19 + java/android/OlmLibSdk/gradlew | 160 ++++++ java/android/OlmLibSdk/gradlew.bat | 90 ++++ java/android/OlmLibSdk/olm-sdk/build.gradle | 72 +++ .../java/org/matrix/olm/OlmAccountTest.java | 223 ++++++++ .../olm-sdk/src/main/AndroidManifest.xml | 11 + .../main/java/org/matrix/olm/OlmAccount.java | 5 +- .../main/java/org/matrix/olm/OlmManager.java | 32 ++ .../main/java/org/matrix/olm/OlmSession.java | 327 ++++++++++++ .../OlmLibSdk/olm-sdk/src/main/jni/Android.mk | 52 ++ .../olm-sdk/src/main/jni/Application.mk | 3 + .../olm-sdk/src/main/jni/olm_account.cpp | 491 ++++++++++++++++++ .../olm-sdk/src/main/jni/olm_account.h | 34 ++ .../OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h | 46 ++ .../olm-sdk/src/main/jni/olm_session.cpp | 476 +++++++++++++++++ .../olm-sdk/src/main/jni/olm_session.h | 38 ++ java/android/OlmLibSdk/settings.gradle | 1 + 18 files changed, 2102 insertions(+), 1 deletion(-) create mode 100644 java/android/OlmLibSdk/build.gradle create mode 100644 java/android/OlmLibSdk/gradle.properties create mode 100644 java/android/OlmLibSdk/gradlew create mode 100644 java/android/OlmLibSdk/gradlew.bat create mode 100644 java/android/OlmLibSdk/olm-sdk/build.gradle create mode 100644 java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java 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 create mode 100644 java/android/OlmLibSdk/settings.gradle diff --git a/java/android/OlmLibSdk/build.gradle b/java/android/OlmLibSdk/build.gradle new file mode 100644 index 0000000..77ce66e --- /dev/null +++ b/java/android/OlmLibSdk/build.gradle @@ -0,0 +1,23 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/java/android/OlmLibSdk/gradle.properties b/java/android/OlmLibSdk/gradle.properties new file mode 100644 index 0000000..13d64a5 --- /dev/null +++ b/java/android/OlmLibSdk/gradle.properties @@ -0,0 +1,19 @@ +## Project-wide Gradle settings. +# +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +#Wed Oct 05 11:49:34 CEST 2016 +systemProp.https.proxyPort=8080 +systemProp.http.proxyHost=batproxy +systemProp.https.proxyHost=batproxy +systemProp.http.proxyPort=8080 diff --git a/java/android/OlmLibSdk/gradlew b/java/android/OlmLibSdk/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/java/android/OlmLibSdk/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/java/android/OlmLibSdk/gradlew.bat b/java/android/OlmLibSdk/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/java/android/OlmLibSdk/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java/android/OlmLibSdk/olm-sdk/build.gradle b/java/android/OlmLibSdk/olm-sdk/build.gradle new file mode 100644 index 0000000..96eaaf7 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/build.gradle @@ -0,0 +1,72 @@ +import org.apache.tools.ant.taskdefs.condition.Os + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 21 + buildToolsVersion '21.1.2' + + defaultConfig { + minSdkVersion 11 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + sourceSets.main { + jniLibs.srcDir 'src/main/libs' + jni.srcDirs = [] + } + + task ndkBuildNative(type: Exec, description: 'NDK building..') { + workingDir file('src/main') + commandLine getNdkBuildCmd() //, '-B', 'NDK_DEBUG=1' + } + + task cleanNative(type: Exec, description: 'Clean NDK build') { + workingDir file('src/main') + commandLine getNdkBuildCmd(), 'clean' + } + + tasks.withType(JavaCompile) { + compileTask -> compileTask.dependsOn ndkBuildNative + } + + clean.dependsOn cleanNative + +} + +def getNdkFolder() { + Properties properties = new Properties() + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + def ndkFolder = properties.getProperty('ndk.dir', null) + if (ndkFolder == null) + throw new GradleException("NDK location missing. Define it with ndk.dir in the local.properties file") + + return ndkFolder +} + +def getNdkBuildCmd() { + def ndkBuildCmd = getNdkFolder() + "/ndk-build" + if (Os.isFamily(Os.FAMILY_WINDOWS)) + ndkBuildCmd += ".cmd" + + return ndkBuildCmd +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile 'com.android.support:appcompat-v7:21.+' + + testCompile 'junit:junit:4.12' + androidTestCompile 'junit:junit:4.12' + androidTestCompile 'com.android.support:support-annotations:21.0.0' + androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:rules:0.5' +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java new file mode 100644 index 0000000..3d7568f --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java @@ -0,0 +1,223 @@ +package org.matrix.olm; + +import android.support.test.runner.AndroidJUnit4; +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class OlmAccountTest { + private static final String LOG_TAG = "OlmAccountTest"; + private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50; + + private static OlmAccount mOlmAccount; + private static OlmManager mOlmManager; + private boolean mIsAccountCreated; + + public static final String TEST_STRING = "This is a string"; + public static final long TEST_LONG = 12345678L; + + @BeforeClass + public static void setUpClass(){ + // load native lib + mOlmManager = new OlmManager(); + + String version = mOlmManager.getOlmLibVersion(); + assertNotNull(version); + Log.d(LOG_TAG, "## setUpClass(): lib version="+version); + } + + @AfterClass + public static void tearDownClass() { + // TBD + } + + @Before + public void setUp() { + if(mIsAccountCreated) { + assertNotNull(mOlmAccount); + } + } + + + @After + public void tearDown() { + // TBD + } + + @Test + public void test1CreateAccount() { + Log.d(LOG_TAG,"## testInitNewAccount"); + mOlmAccount = new OlmAccount(); + assertNotNull(mOlmAccount); + } + + @Test + public void test2InitNewAccount() { + Log.d(LOG_TAG,"## testInitNewAccount"); + assertTrue(mOlmAccount.initNewAccount()); + mIsAccountCreated = true; + } + + @Test + public void test3GetOlmAccountId() { + Log.d(LOG_TAG,"## testGetOlmAccountId"); + + long olmNativeInstance = mOlmAccount.getOlmAccountId(); + assertTrue(0!=olmNativeInstance); + } + + @Test + public void test4IdentityKeys() { + Log.d(LOG_TAG,"## testIdentityKeys"); + + JSONObject identityKeysJson = mOlmAccount.identityKeys(); + assertNotNull(identityKeysJson); + Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson); + + try { + String fingerPrintKey = identityKeysJson.getString(OlmAccount.JSON_KEY_FINGER_PRINT_KEY); + assertFalse("fingerprint key missing",TextUtils.isEmpty(fingerPrintKey)); + } catch (JSONException e) { + e.printStackTrace(); + assertTrue("Exception MSg="+e.getMessage(), false); + } + + try { + String identityKey = identityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY); + assertFalse("identity key missing",TextUtils.isEmpty(identityKey)); + } catch (JSONException e) { + e.printStackTrace(); + assertTrue("Exception MSg="+e.getMessage(), false); + } + + + } + + + //**************************************************** + //** ************** One time keys TESTS ************** + //**************************************************** + @Test + public void test5MaxOneTimeKeys() { + Log.d(LOG_TAG,"## testMaxOneTimeKeys"); + + long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys(); + Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys); + + assertTrue(maxOneTimeKeys>0); + } + + @Test + public void test6GenerateOneTimeKeys() { + Log.d(LOG_TAG,"## testGenerateOneTimeKeys"); + int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER); + assertTrue(0==retValue); + } + + @Test + public void test7OneTimeKeysJsonFormat() { + Log.d(LOG_TAG,"## testIdentityKeys"); + JSONObject generatedKeysJsonObj; + JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys(); + assertNotNull(oneTimeKeysJson); + + try { + generatedKeysJsonObj = oneTimeKeysJson.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY); + assertFalse(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null==generatedKeysJsonObj); + + /*String oneTimeKeyA = generatedKeysJsonObj.getString(OlmAccount.JSON_KEY_ONE_TIME_KEY_GENERATED_A); + assertFalse(" one time KeyA object is missing", TextUtils.isEmpty(oneTimeKeyA)); + + String oneTimeKeyB = generatedKeysJsonObj.getString(OlmAccount.JSON_KEY_ONE_TIME_KEY_GENERATED_B); + assertFalse(" one time KeyA object is missing", TextUtils.isEmpty(oneTimeKeyA));*/ + } catch (JSONException e) { + assertTrue("Exception MSg="+e.getMessage(), false); + } + } + + // TODO testRemoveOneTimeKeysForSession when session is available + /*@Test + public void testRemoveOneTimeKeysForSession() { + Log.d(LOG_TAG,"## testRemoveOneTimeKeysForSession"); + OLMSession olmSession = new OLMSession(); + + JSONArray keysJsonArray = mOlmAccount.removeOneTimeKeysForSession(olmSession); + + assertNotNull(keysJsonArray); + // TODO add extra test to test the JSON content format.. + }*/ + + @Test + public void test8MarkOneTimeKeysAsPublished() { + Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished"); + + int retCode = mOlmAccount.markOneTimeKeysAsPublished(); + // if OK => retCode=0 + assertTrue(0 == retCode); + } + + @Test + public void test9SignMessage() { + Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished"); + + String clearMsg = "String to be signed by olm"; + String signedMsg = mOlmAccount.signMessage(clearMsg); + assertNotNull(signedMsg); + // TODO add test to unsign the signedMsg and compare it ot clearMsg + } + + + private void testJni(){ + OlmManager mgr = new OlmManager(); + String versionLib = mgr.getOlmLibVersion(); + Log.d(LOG_TAG, "## testJni(): lib version="+versionLib); + + OlmAccount account = new OlmAccount(); + boolean initStatus = account.initNewAccount(); + + long accountNativeId = account.getOlmAccountId(); + Log.d(LOG_TAG, "## testJni(): lib accountNativeId="+accountNativeId); + + JSONObject identityKeys = account.identityKeys(); + Log.d(LOG_TAG, "## testJni(): identityKeysJson="+identityKeys.toString()); + + long maxOneTimeKeys = account.maxOneTimeKeys(); + Log.d(LOG_TAG, "## testJni(): lib maxOneTimeKeys="+maxOneTimeKeys); + + int generateRetCode = account.generateOneTimeKeys(50); + Log.d(LOG_TAG, "## testJni(): generateRetCode="+generateRetCode); + + JSONObject onteTimeKeysKeysJson = account.oneTimeKeys(); + Log.d(LOG_TAG, "## testJni(): onteTimeKeysKeysJson="+onteTimeKeysKeysJson.toString()); + + // TODO removeOneTimeKeysForSession(session); + + int asPublishedRetCode = account.markOneTimeKeysAsPublished(); + Log.d(LOG_TAG, "## testJni(): asPublishedRetCode="+asPublishedRetCode); + + String clearMsg ="My clear message"; + String signedMsg = account.signMessage(clearMsg); + Log.d(LOG_TAG, "## testJni(): signedMsg="+signedMsg); + + account.releaseAccount(); + } + + +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml b/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8a8747f --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + 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 299a07d..ac8e12d 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,7 +26,10 @@ public class OlmAccount { // JSON keys used in the JSON objects returned by JNI public static String JSON_KEY_ONE_TIME_KEY = "curve25519"; + public static String JSON_KEY_IDENTITY_KEY = "curve25519"; public static String JSON_KEY_FINGER_PRINT_KEY = "ed25519"; + public static String JSON_KEY_ONE_TIME_KEY_GENERATED_A = "AAAAAA"; + public static String JSON_KEY_ONE_TIME_KEY_GENERATED_B = "AAAAAB"; /** instance unique identifier, used in JNI to match the corresponding native class **/ private int mJavaInstanceId; @@ -99,7 +102,7 @@ public class OlmAccount { private native byte[] identityKeysJni(); /** - * Return the identity keys in a JSON array.
+ * Return the identity keys (identity & fingerprint keys) in a JSON array.
* Public API for {@link #identityKeysJni()}. * @return identity keys in JSON array format if operation succeed, null otherwise */ 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 new file mode 100644 index 0000000..55b9910 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.olm; + + +public class OlmManager { + + static { + java.lang.System.loadLibrary("olm"); + } + + /** + * Get the OLM lib version. + * @return the lib version as a string + */ + public native String getOlmLibVersion(); +} + 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 new file mode 100644 index 0000000..28324da --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java @@ -0,0 +1,327 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.olm; + +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +public class OlmSession { + private static final String LOG_TAG = "OlmSession"; + /** session raw pointer value (OlmSession*) returned by JNI. + * this value uniquely identifies the native session instance. + **/ + private long mNativeOlmSessionId; + + /** account instance associated with this session. **/ + private OlmAccount mOlmAccount; + + public OlmSession() { + //initNewSession(); + } + + /** + * Getter on the session ID. + * @return native session ID + */ + public long getOlmSessionId(){ + return mNativeOlmSessionId; + } + + /** + * Destroy the corresponding OLM session native object.
+ * This method must ALWAYS be called when this JAVA instance + * is destroyed (ie. garbage collected) to prevent memory leak in native side. + * See {@link #initNewSessionJni()}. + */ + private native void releaseSessionJni(); + + /** + * Release native session and invalid its JAVA reference counter part.
+ * Public API for {@link #releaseSessionJni()}. + * To be called before any other API call. + */ + public void releaseSession(){ + releaseSessionJni(); + + mNativeOlmSessionId = 0; + } + + + /** + * Create and save the session native instance ID. + * Wrapper for {@link #initNewSessionJni()}.
+ * To be called before any other API call. + * @return true if init succeed, false otherwise. + */ + public boolean initNewSession() { + boolean retCode = false; + if(0 != (mNativeOlmSessionId = initNewSessionJni())){ + retCode = true; + } + return retCode; + } + + /** + * Create the corresponding OLM session in native side.
+ * The return value is a long casted C ptr on the OlmSession. + * Do not forget to call {@link #releaseSession()} when JAVA side is done. + * @return native session instance identifier (see {@link #mNativeOlmSessionId}) + */ + private native long initNewSessionJni(); + + + /** + * Creates a new out-bound session for sending messages to a recipient + * identified by an identity key and a one time key.
+ * Public API for {@link #initOutboundSessionWithAccount(OlmAccount, String, String)}. + * @param aAccount the account to associate with this session + * @param aTheirIdentityKey the identity key of the recipient + * @param aTheirOneTimeKey the one time key of the recipient + * @return this if operation succeed, null otherwise + */ + public OlmSession initOutboundSessionWithAccount(OlmAccount aAccount, String aTheirIdentityKey, String aTheirOneTimeKey) { + OlmSession retObj=null; + + if((null==aAccount) || TextUtils.isEmpty(aTheirIdentityKey) || TextUtils.isEmpty(aTheirOneTimeKey)){ + Log.e(LOG_TAG, "## initOutboundSession(): invalid input parameters"); + } else { + // set the account of this session + mOlmAccount = aAccount; + + int retCode = initOutboundSessionJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aTheirOneTimeKey); + retObj = this; + } + + return retObj; + } + + private native int initOutboundSessionJni(long aOlmAccountId, String aTheirIdentityKey, String aTheirOneTimeKey); + + + /** + * Create a new in-bound session for sending/receiving messages from an + * incoming PRE_KEY message.
+ * Public API for {@link #initInboundSessionJni(long, String)}. + * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). + * @param aAccount the account to associate with this session + * @param aOneTimeKeyMsg PRE KEY message TODO TBC + * @return this if operation succeed, null otherwise + */ + public OlmSession initInboundSessionWithAccount(OlmAccount aAccount, String aOneTimeKeyMsg) { + OlmSession retObj=null; + + if((null==aAccount) || TextUtils.isEmpty(aOneTimeKeyMsg)){ + Log.e(LOG_TAG, "## initInboundSessionWithAccount(): invalid input parameters"); + } else { + // set the account of this session + mOlmAccount = aAccount; + + if( 0 == initInboundSessionJni(mOlmAccount.getOlmAccountId(), aOneTimeKeyMsg)) { + retObj = this; + } + } + + return retObj; + } + + private native int initInboundSessionJni(long aOlmAccountId, String aOneTimeKeyMsg); + + + /** + * Create a new in-bound session for sending/receiving messages from an + * incoming PRE_KEY message based on the sender identity key TODO TBC!.
+ * Public API for {@link #initInboundSessionFromIdKeyJni(long, String, String)}. + * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). + * @param aAccount the account to associate with this session + * @param aTheirIdentityKey the sender identity key + * @param aOneTimeKeyMsg PRE KEY message TODO TBC + * @return this if operation succeed, null otherwise + */ + public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aOneTimeKeyMsg) { + OlmSession retObj=null; + + if((null==aAccount) || TextUtils.isEmpty(aOneTimeKeyMsg)){ + Log.e(LOG_TAG, "## initInboundSessionWithAccount(): invalid input parameters"); + } else { + // set the account of this session + mOlmAccount = aAccount; + + if(0 == initInboundSessionFromIdKeyJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aOneTimeKeyMsg)){ + retObj = this; + } + } + + return retObj; + } + + private native int initInboundSessionFromIdKeyJni(long aOlmAccountId, String aTheirIdentityKey, String aOneTimeKeyMsg); + + + /** + * 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). + * Public API for {@link #matchesInboundSessionJni(String)}. + * @param aOneTimeKeyMsg PRE KEY message + * @return this if operation succeed, null otherwise + */ + public boolean matchesInboundSession(String aOneTimeKeyMsg) { + boolean retCode = false; + + if(0 == matchesInboundSessionJni(aOneTimeKeyMsg)){ + retCode = true; + } + return retCode; + } + + private native int matchesInboundSessionJni(String aOneTimeKeyMsg); + + + /** + * Get the session identifier.
Will be the same for both ends of the + * conversation. The session identifier is returned as a String object. + * Session Id sample: "session_id":"M4fOVwD6AABrkTKl" + * Public API for {@link #getSessionIdentifierJni()}. + * @return the session ID as a String if operation succeed, null otherwise + */ + public String sessionIdentifier() { + return getSessionIdentifierJni(); + } + + private native String getSessionIdentifierJni(); + +/* +- (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage; +- (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage; + +// UTF-8 plaintext -> base64 ciphertext +- (OLMMessage*) encryptMessage:(NSString*)message; + +// base64 ciphertext -> UTF-8 plaintext +- (NSString*) decryptMessage:(OLMMessage*)message; +*/ + + + /** + * Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).
+ * Keys are Base64 encoded. + * These keys must be published on the server. + * @return byte array containing the identity keys if operation succeed, null otherwise + */ + private native byte[] identityKeysJni(); + + /** + * Return the identity keys in a JSON array.
+ * Public API for {@link #identityKeysJni()}. + * @return identity keys in JSON array format if operation succeed, null otherwise + */ + public JSONObject identityKeys() { + JSONObject identityKeysJsonObj = null; + byte identityKeysBuffer[]; + + if( null != (identityKeysBuffer = identityKeysJni())) { + try { + identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer)); + Log.d(LOG_TAG, "## identityKeys(): Identity Json keys=" + identityKeysJsonObj.toString()); + } catch (JSONException e) { + identityKeysJsonObj = null; + Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage()); + } + } else { + Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null"); + } + + return identityKeysJsonObj; + } + + /** + * Return the largest number of "one time keys" this account can store. + * @return the max number of "one time keys", -1 otherwise + */ + public native long maxOneTimeKeys(); + + /** + * Generate a number of new one time keys.
If total number of keys stored + * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded. + * @param aNumberOfKeys number of keys to generate + * @return 0 if operation succeed, -1 otherwise + */ + public native int generateOneTimeKeys(int aNumberOfKeys); + + /** + * Get the public parts of the unpublished "one time keys" for the account.
+ * The returned data is a JSON-formatted object with the single property + * curve25519, which is itself an object mapping key id to + * base64-encoded Curve25519 key. + * These keys must be published on the server. + * @return byte array containing the one time keys if operation succeed, null otherwise + */ + private native byte[] oneTimeKeysJni(); + + /** + * Return the "one time keys" in a JSON array.
+ * Public API for {@link #oneTimeKeysJni()}. + * @return one time keys in JSON array format if operation succeed, null otherwise + */ + public JSONObject oneTimeKeys() { + byte identityKeysBuffer[]; + JSONObject identityKeysJsonObj = null; + + if( null != (identityKeysBuffer = oneTimeKeysJni())) { + try { + identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer)); + Log.d(LOG_TAG, "## oneTimeKeys(): Identity Json keys=" + identityKeysJsonObj.toString()); + } catch (JSONException e) { + identityKeysJsonObj = null; + Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage()); + } + } else { + Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null"); + } + + return identityKeysJsonObj; + } + + /** + * Remove the "one time keys" that the session used from the account. + * @param aNativeOlmSessionId native session instance identifier + * @return 0 if operation succeed, 1 if no matching keys in the sessions to be removed, -1 if operation failed + */ + public native int removeOneTimeKeysForSession(long aNativeOlmSessionId); + + /** + * Marks the current set of "one time keys" as being published. + * @return 0 if operation succeed, -1 otherwise + */ + public native int markOneTimeKeysAsPublished(); + + /** + * Sign a message with the ed25519 fingerprint key for this account. + * @param aMessage message to sign + * @return the signed message if operation succeed, null otherwise + */ + public native String signMessage(String aMessage); + + @Override + public String toString() { + return super.toString(); + } + +} + 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 diff --git a/java/android/OlmLibSdk/settings.gradle b/java/android/OlmLibSdk/settings.gradle new file mode 100644 index 0000000..d11302c --- /dev/null +++ b/java/android/OlmLibSdk/settings.gradle @@ -0,0 +1 @@ +include ':olm-sdk' From 573713dd000323208e0b520e39229e60e7699ac5 Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Thu, 6 Oct 2016 08:40:21 +0200 Subject: [PATCH 3/9] - Add missing file --- java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml b/java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml new file mode 100644 index 0000000..93bea1d --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + OlmSdk + From 0393ad68438669f60a6679c9e0f8010c7366c5ed Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Thu, 6 Oct 2016 10:30:24 +0200 Subject: [PATCH 4/9] Update Account unit tests --- .../java/org/matrix/olm/OlmAccountTest.java | 25 +++++++++++-------- .../main/java/org/matrix/olm/OlmAccount.java | 8 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java index 3d7568f..061c79d 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java @@ -15,9 +15,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; -import static org.junit.Assert.assertFalse; +import java.util.Iterator; + import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4.class) @@ -93,7 +93,7 @@ public class OlmAccountTest { try { String fingerPrintKey = identityKeysJson.getString(OlmAccount.JSON_KEY_FINGER_PRINT_KEY); - assertFalse("fingerprint key missing",TextUtils.isEmpty(fingerPrintKey)); + assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey)); } catch (JSONException e) { e.printStackTrace(); assertTrue("Exception MSg="+e.getMessage(), false); @@ -101,7 +101,7 @@ public class OlmAccountTest { try { String identityKey = identityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY); - assertFalse("identity key missing",TextUtils.isEmpty(identityKey)); + assertTrue("identity key missing",!TextUtils.isEmpty(identityKey)); } catch (JSONException e) { e.printStackTrace(); assertTrue("Exception MSg="+e.getMessage(), false); @@ -110,7 +110,6 @@ public class OlmAccountTest { } - //**************************************************** //** ************** One time keys TESTS ************** //**************************************************** @@ -133,20 +132,24 @@ public class OlmAccountTest { @Test public void test7OneTimeKeysJsonFormat() { - Log.d(LOG_TAG,"## testIdentityKeys"); + Log.d(LOG_TAG,"## test7OneTimeKeysJsonFormat"); + int oneTimeKeysCount = 0; JSONObject generatedKeysJsonObj; JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys(); assertNotNull(oneTimeKeysJson); try { generatedKeysJsonObj = oneTimeKeysJson.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY); - assertFalse(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null==generatedKeysJsonObj); + assertTrue(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null!=generatedKeysJsonObj); - /*String oneTimeKeyA = generatedKeysJsonObj.getString(OlmAccount.JSON_KEY_ONE_TIME_KEY_GENERATED_A); - assertFalse(" one time KeyA object is missing", TextUtils.isEmpty(oneTimeKeyA)); + // test the count of the generated one time keys: + Iterator generatedKeysIt = generatedKeysJsonObj.keys(); + while(generatedKeysIt.hasNext()){ + generatedKeysIt.next(); + oneTimeKeysCount++; + } + assertTrue("Expected count="+GENERATION_ONE_TIME_KEYS_NUMBER+" found="+oneTimeKeysCount,GENERATION_ONE_TIME_KEYS_NUMBER==oneTimeKeysCount); - String oneTimeKeyB = generatedKeysJsonObj.getString(OlmAccount.JSON_KEY_ONE_TIME_KEY_GENERATED_B); - assertFalse(" one time KeyA object is missing", TextUtils.isEmpty(oneTimeKeyA));*/ } catch (JSONException e) { assertTrue("Exception MSg="+e.getMessage(), false); } 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 ac8e12d..15de09c 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 @@ -25,11 +25,9 @@ public class OlmAccount { private static final String LOG_TAG = "OlmAccount"; // JSON keys used in the JSON objects returned by JNI - public static String JSON_KEY_ONE_TIME_KEY = "curve25519"; - public static String JSON_KEY_IDENTITY_KEY = "curve25519"; - public static String JSON_KEY_FINGER_PRINT_KEY = "ed25519"; - public static String JSON_KEY_ONE_TIME_KEY_GENERATED_A = "AAAAAA"; - public static String JSON_KEY_ONE_TIME_KEY_GENERATED_B = "AAAAAB"; + public static final String JSON_KEY_ONE_TIME_KEY = "curve25519"; + public static final String JSON_KEY_IDENTITY_KEY = "curve25519"; + public static final String JSON_KEY_FINGER_PRINT_KEY = "ed25519"; /** instance unique identifier, used in JNI to match the corresponding native class **/ private int mJavaInstanceId; From 655c841cc3720d1bb9892d60a2d7ca136c90cfbd Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Thu, 6 Oct 2016 19:55:03 +0200 Subject: [PATCH 5/9] - Update Unit tests for OlmAccount - new file olm_utility.cpp to have a stand alone function to initialize/alloc a random buffer - new class OlmMessage - complete OlmSession API with encryptMessage() - comments review - OlmAccount unit tests are green - new gradle to compile the shared lib according to debug mode --- java/android/OlmLibSdk/olm-sdk/build.gradle | 24 ++- .../java/org/matrix/olm/OlmAccountTest.java | 16 +- .../main/java/org/matrix/olm/OlmMessage.java | 30 +++ .../main/java/org/matrix/olm/OlmSession.java | 150 ++++----------- .../OlmLibSdk/olm-sdk/src/main/jni/Android.mk | 3 +- .../olm-sdk/src/main/jni/olm_account.cpp | 56 +++--- .../OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h | 2 +- .../olm-sdk/src/main/jni/olm_session.cpp | 174 ++++++++++++++++-- .../olm-sdk/src/main/jni/olm_session.h | 2 + .../olm-sdk/src/main/jni/olm_utility.cpp | 60 ++++++ .../olm-sdk/src/main/jni/olm_utility.h | 16 ++ 11 files changed, 355 insertions(+), 178 deletions(-) create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h diff --git a/java/android/OlmLibSdk/olm-sdk/build.gradle b/java/android/OlmLibSdk/olm-sdk/build.gradle index 96eaaf7..2f92151 100644 --- a/java/android/OlmLibSdk/olm-sdk/build.gradle +++ b/java/android/OlmLibSdk/olm-sdk/build.gradle @@ -24,9 +24,16 @@ android { jni.srcDirs = [] } - task ndkBuildNative(type: Exec, description: 'NDK building..') { + task ndkBuildNativeRelease(type: Exec, description: 'NDK building..') { + println 'ndkBuildNativeRelease starts..' workingDir file('src/main') - commandLine getNdkBuildCmd() //, '-B', 'NDK_DEBUG=1' + commandLine getNdkBuildCmd(), 'NDK_DEBUG=0' + } + + task ndkBuildNativeDebug(type: Exec, description: 'NDK building..') { + println 'ndkBuildNativeDebug starts..' + workingDir file('src/main') + commandLine getNdkBuildCmd(), 'NDK_DEBUG=1' } task cleanNative(type: Exec, description: 'Clean NDK build') { @@ -34,10 +41,21 @@ android { commandLine getNdkBuildCmd(), 'clean' } - tasks.withType(JavaCompile) { + /*tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuildNative + }*/ + + tasks.withType(JavaCompile) { + compileTask -> if (compileTask.name.startsWith('compileDebugJava')) { + println 'test compile: Debug' + compileTask.dependsOn ndkBuildNativeDebug + } else if (compileTask.name.startsWith('compileReleaseJava')) { + println 'test compile: Release' + compileTask.dependsOn ndkBuildNativeRelease + } } + clean.dependsOn cleanNative } diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java index 061c79d..f592aa4 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java @@ -55,7 +55,6 @@ public class OlmAccountTest { } } - @After public void tearDown() { // TBD @@ -63,30 +62,25 @@ public class OlmAccountTest { @Test public void test1CreateAccount() { - Log.d(LOG_TAG,"## testInitNewAccount"); mOlmAccount = new OlmAccount(); assertNotNull(mOlmAccount); } @Test public void test2InitNewAccount() { - Log.d(LOG_TAG,"## testInitNewAccount"); assertTrue(mOlmAccount.initNewAccount()); mIsAccountCreated = true; } @Test public void test3GetOlmAccountId() { - Log.d(LOG_TAG,"## testGetOlmAccountId"); - long olmNativeInstance = mOlmAccount.getOlmAccountId(); + Log.d(LOG_TAG,"## testGetOlmAccountId olmNativeInstance="+olmNativeInstance); assertTrue(0!=olmNativeInstance); } @Test public void test4IdentityKeys() { - Log.d(LOG_TAG,"## testIdentityKeys"); - JSONObject identityKeysJson = mOlmAccount.identityKeys(); assertNotNull(identityKeysJson); Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson); @@ -115,8 +109,6 @@ public class OlmAccountTest { //**************************************************** @Test public void test5MaxOneTimeKeys() { - Log.d(LOG_TAG,"## testMaxOneTimeKeys"); - long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys(); Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys); @@ -125,14 +117,12 @@ public class OlmAccountTest { @Test public void test6GenerateOneTimeKeys() { - Log.d(LOG_TAG,"## testGenerateOneTimeKeys"); int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER); assertTrue(0==retValue); } @Test public void test7OneTimeKeysJsonFormat() { - Log.d(LOG_TAG,"## test7OneTimeKeysJsonFormat"); int oneTimeKeysCount = 0; JSONObject generatedKeysJsonObj; JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys(); @@ -169,8 +159,6 @@ public class OlmAccountTest { @Test public void test8MarkOneTimeKeysAsPublished() { - Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished"); - int retCode = mOlmAccount.markOneTimeKeysAsPublished(); // if OK => retCode=0 assertTrue(0 == retCode); @@ -178,8 +166,6 @@ public class OlmAccountTest { @Test public void test9SignMessage() { - Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished"); - String clearMsg = "String to be signed by olm"; String signedMsg = mOlmAccount.signMessage(clearMsg); assertNotNull(signedMsg); diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java new file mode 100644 index 0000000..047db97 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.olm; + +public class OlmMessage { + /** PRE KEY message type (used to establish new Olm session) **/ + public final static int MESSAGE_TYPE_PRE_KEY = 0; + /** normal message type **/ + public final static int MESSAGE_TYPE_MESSAGE = 1; + + /** the encrypted message (ie. )**/ + public String mCipherText; + + /** defined by {@link #MESSAGE_TYPE_MESSAGE} or {@link #MESSAGE_TYPE_PRE_KEY}**/ + public long mType; +} 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 28324da..24a4215 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 @@ -146,12 +146,12 @@ public class OlmSession { /** * Create a new in-bound session for sending/receiving messages from an - * incoming PRE_KEY message based on the sender identity key TODO TBC!.
+ * incoming PRE_KEY message based on the sender identity key.
* Public API for {@link #initInboundSessionFromIdKeyJni(long, String, String)}. * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). * @param aAccount the account to associate with this session * @param aTheirIdentityKey the sender identity key - * @param aOneTimeKeyMsg PRE KEY message TODO TBC + * @param aOneTimeKeyMsg PRE KEY message * @return this if operation succeed, null otherwise */ public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aOneTimeKeyMsg) { @@ -173,6 +173,18 @@ public class OlmSession { private native int initInboundSessionFromIdKeyJni(long aOlmAccountId, String aTheirIdentityKey, String aOneTimeKeyMsg); + /** + * Get the session identifier.
Will be the same for both ends of the + * conversation. The session identifier is returned as a String object. + * Session Id sample: "session_id":"M4fOVwD6AABrkTKl" + * Public API for {@link #getSessionIdentifierJni()}. + * @return the session ID as a String if operation succeed, null otherwise + */ + public String sessionIdentifier() { + return getSessionIdentifierJni(); + } + + private native String getSessionIdentifierJni(); /** * Checks if the PRE_KEY message is for this in-bound session.
@@ -194,129 +206,43 @@ public class OlmSession { /** - * Get the session identifier.
Will be the same for both ends of the - * conversation. The session identifier is returned as a String object. - * Session Id sample: "session_id":"M4fOVwD6AABrkTKl" - * Public API for {@link #getSessionIdentifierJni()}. - * @return the session ID as a String if operation succeed, null otherwise + * 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). + * Public API for {@link #matchesInboundSessionJni(String)}. + * @param aTheirIdentityKey the sender identity key + * @param aOneTimeKeyMsg PRE KEY message + * @return this if operation succeed, null otherwise */ - public String sessionIdentifier() { - return getSessionIdentifierJni(); + public boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) { + boolean retCode = false; + + if(0 == matchesInboundSessionFromIdKeyJni(aTheirIdentityKey, aOneTimeKeyMsg)){ + retCode = true; + } + return retCode; } - private native String getSessionIdentifierJni(); - -/* -- (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage; -- (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage; - -// UTF-8 plaintext -> base64 ciphertext -- (OLMMessage*) encryptMessage:(NSString*)message; - -// base64 ciphertext -> UTF-8 plaintext -- (NSString*) decryptMessage:(OLMMessage*)message; -*/ + private native int matchesInboundSessionFromIdKeyJni(String aTheirIdentityKey, String aOneTimeKeyMsg); /** - * Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).
- * Keys are Base64 encoded. - * These keys must be published on the server. - * @return byte array containing the identity keys if operation succeed, null otherwise + * Encrypt a message using the session.
+ * Public API for {@link #encryptMessageJni(String, OlmMessage)}. + * @param aClearMsg message to encrypted + * @return the encrypted message if operation succeed, null otherwise */ - private native byte[] identityKeysJni(); + public OlmMessage encryptMessage(String aClearMsg) { + OlmMessage encryptedMsgRetValue = new OlmMessage(); - /** - * Return the identity keys in a JSON array.
- * Public API for {@link #identityKeysJni()}. - * @return identity keys in JSON array format if operation succeed, null otherwise - */ - public JSONObject identityKeys() { - JSONObject identityKeysJsonObj = null; - byte identityKeysBuffer[]; - - if( null != (identityKeysBuffer = identityKeysJni())) { - try { - identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer)); - Log.d(LOG_TAG, "## identityKeys(): Identity Json keys=" + identityKeysJsonObj.toString()); - } catch (JSONException e) { - identityKeysJsonObj = null; - Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage()); - } - } else { - Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null"); + if(0 != encryptMessageJni(aClearMsg, encryptedMsgRetValue)){ + encryptedMsgRetValue = null; } - return identityKeysJsonObj; + return encryptedMsgRetValue; } - /** - * Return the largest number of "one time keys" this account can store. - * @return the max number of "one time keys", -1 otherwise - */ - public native long maxOneTimeKeys(); + private native int encryptMessageJni(String aClearMsg, OlmMessage aEncryptedMsg); - /** - * Generate a number of new one time keys.
If total number of keys stored - * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded. - * @param aNumberOfKeys number of keys to generate - * @return 0 if operation succeed, -1 otherwise - */ - public native int generateOneTimeKeys(int aNumberOfKeys); - - /** - * Get the public parts of the unpublished "one time keys" for the account.
- * The returned data is a JSON-formatted object with the single property - * curve25519, which is itself an object mapping key id to - * base64-encoded Curve25519 key. - * These keys must be published on the server. - * @return byte array containing the one time keys if operation succeed, null otherwise - */ - private native byte[] oneTimeKeysJni(); - - /** - * Return the "one time keys" in a JSON array.
- * Public API for {@link #oneTimeKeysJni()}. - * @return one time keys in JSON array format if operation succeed, null otherwise - */ - public JSONObject oneTimeKeys() { - byte identityKeysBuffer[]; - JSONObject identityKeysJsonObj = null; - - if( null != (identityKeysBuffer = oneTimeKeysJni())) { - try { - identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer)); - Log.d(LOG_TAG, "## oneTimeKeys(): Identity Json keys=" + identityKeysJsonObj.toString()); - } catch (JSONException e) { - identityKeysJsonObj = null; - Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage()); - } - } else { - Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null"); - } - - return identityKeysJsonObj; - } - - /** - * Remove the "one time keys" that the session used from the account. - * @param aNativeOlmSessionId native session instance identifier - * @return 0 if operation succeed, 1 if no matching keys in the sessions to be removed, -1 if operation failed - */ - public native int removeOneTimeKeysForSession(long aNativeOlmSessionId); - - /** - * Marks the current set of "one time keys" as being published. - * @return 0 if operation succeed, -1 otherwise - */ - public native int markOneTimeKeysAsPublished(); - - /** - * Sign a message with the ed25519 fingerprint key for this account. - * @param aMessage message to sign - * @return the signed message if operation succeed, null otherwise - */ - public native String signMessage(String aMessage); @Override public String toString() { 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 d59f916..26a6a90 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk @@ -44,7 +44,8 @@ $(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 +olm_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 d8ee409..51c0ca8 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 @@ -15,7 +15,7 @@ */ #include "olm_account.h" - +#include "olm_utility.h" /** * Init memory allocation for account creation. @@ -70,10 +70,10 @@ JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv * **/ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv *env, jobject thiz) { - OlmAccount* accountPtr = NULL; + OlmAccount *accountPtr = NULL; + uint8_t *randomBuffPtr = NULL; size_t accountRetCode; - uint8_t* randomBuffPtr = NULL; - int randomSize; + size_t randomSize; // init account memory allocation if(NULL == (accountPtr = initializeAccountMemory())) @@ -84,36 +84,34 @@ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv { // allocate random buffer randomSize = olm_create_account_random_length(accountPtr); - if(NULL == (randomBuffPtr = (std::uint8_t*)malloc(randomSize*sizeof(std::uint8_t)))) + if(false == setRandomInBuffer(&randomBuffPtr, randomSize)) { - LOGE("## initNewAccount(): failure - random buffer OOM"); + LOGE("## initNewAccount(): failure - random buffer init"); } else - { // create random buffer - LOGD("## initNewAccount(): randomSize=%d",randomSize); - - srand(time(NULL)); // init seed - for(int i=0;iGetStringUTFChars(aTheirIdentityKey, 0))) { LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM"); @@ -136,7 +136,14 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI 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); + sessionResult = olm_create_outbound_session(sessionPtr, + accountPtr, + theirIdentityKeyPtr, + theirIdentityKeyLength, + theirOneTimeKeyPtr, + theirOneTimeKeyLength, + (void*)randomBuffPtr, + randomSize); if(sessionResult == olm_error()) { const char *errorMsgPtr = olm_session_last_error(sessionPtr); LOGE("## initOutboundSessionJni(): failure - session creation Msg=%s",errorMsgPtr); @@ -147,20 +154,25 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI 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); - } } } + // **** free mem alloc *** + if(NULL!= randomBuffPtr) + { + free(randomBuffPtr); + } + + if(NULL!= theirIdentityKeyPtr) + { + env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr); + } + + if(NULL!= theirOneTimeKeyPtr) + { + env->ReleaseStringUTFChars(aTheirOneTimeKey, theirOneTimeKeyPtr); + } + return retCode; } @@ -172,7 +184,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI * 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 + * @param aOneTimeKeyMsg PRE_KEY message * @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) @@ -337,6 +349,12 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(J } } + // free local alloc + if(NULL!= messagePtr) + { + env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr); + } + return retCode; } @@ -392,10 +410,128 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromI } } + // free local alloc + if(NULL!= theirIdentityKeyPtr) + { + env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr); + } + + if(NULL!= messagePtr) + { + env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr); + } + return retCode; } +/** + * Encrypt a message using the session. to a base64 ciphertext.
+ * 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_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg) +{ + jint retCode = ERROR_CODE_KO; + OlmSession *sessionPtr = NULL; + const char *clearMsgPtr = NULL; + uint8_t *randomBuffPtr = NULL; + void *encryptedMsgPtr = NULL; + jclass encryptedMsgJClass; + jfieldID encryptedMsgFieldId; + jfieldID typeMsgFieldId; + + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## encryptMessageJni(): failure - invalid Session ptr=NULL"); + } + else if(0 == aClearMsg) + { + LOGE("## encryptMessageJni(): failure - invalid clear message"); + } + else if(0 == aEncryptedMsg) + { + LOGE("## encryptMessageJni(): failure - invalid clear message"); + } + else if(NULL == (clearMsgPtr = env->GetStringUTFChars(aClearMsg, 0))) + { + LOGE("## encryptMessageJni(): failure - clear message JNI allocation OOM"); + } + else if(0 == (encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg))) + { + LOGE("## encryptMessageJni(): failure - unable to get crypted message class"); + } + else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mCipherText","Ljava/lang/String;"))) + { + LOGE("## encryptMessageJni(): failure - unable to get message field"); + } + else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","I"))) + { + LOGE("## encryptMessageJni(): failure - unable to get message type field"); + } + else + { + // compute random buffer + size_t randomLength = olm_encrypt_random_length(sessionPtr); + if(false == setRandomInBuffer(&randomBuffPtr, randomLength)) + { + LOGE("## encryptMessageJni(): failure - random buffer init"); + } + else + { + // alloc buffer for encrypted message + size_t clearMsgLength = env->GetStringUTFLength(aClearMsg); + size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength); + if(NULL == (encryptedMsgPtr = (void*)malloc(encryptedMsgLength*sizeof(void*)))) + { + LOGE("## encryptMessageJni(): failure - random buffer OOM"); + } + + size_t result = olm_encrypt(sessionPtr, + (void const *)clearMsgPtr, + clearMsgLength, + randomBuffPtr, + randomLength, + encryptedMsgPtr, + encryptedMsgLength); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## encryptMessageJni(): failure - Msg=%s",errorMsgPtr); + } + else + { + // update type: PRE KEY message or normal message + size_t messageType = olm_encrypt_message_type(sessionPtr); + env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType); + + // update message + jstring encryptedStr = env->NewStringUTF((const char*)encryptedMsgPtr); + env->SetObjectField(aEncryptedMsg, encryptedMsgFieldId, (jobject)encryptedStr); + + retCode = ERROR_CODE_OK; + LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%s", result, messageType, (const char*)encryptedMsgPtr); + } + } + } + + // free alloc + if(NULL != clearMsgPtr) + { + env->ReleaseStringUTFChars(aClearMsg, clearMsgPtr); + } + + if(NULL != randomBuffPtr) + { + free(randomBuffPtr); + } + + return retCode; +} + /** * Get the session identifier for this session. * @return the session identifier if operation succeed, null otherwise 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 index edd1012..8e162b0 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h @@ -24,6 +24,8 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKe 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 jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg); + JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz); diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp new file mode 100644 index 0000000..9abd228 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp @@ -0,0 +1,60 @@ +/** + * Created by pedrocon on 06/10/2016. + */ +/* + * 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_jni.h" +#include "olm_utility.h" + +/** +* Init a buffer with a given number of random values. +* @param aBuffer2Ptr the buffer to be initialized +* @param aRandomSize the number of random values to apply +* @return true if operation succeed, false otherwise +**/ +bool setRandomInBuffer(uint8_t **aBuffer2Ptr, size_t aRandomSize) +{ + bool retCode = false; + if(NULL == aBuffer2Ptr) + { + LOGD("## setRandomInBuffer(): failure - aBuffer=NULL"); + } + else if(0 == aRandomSize) + { + LOGD("## setRandomInBuffer(): failure - random size=0"); + } + else if(NULL == (*aBuffer2Ptr = (uint8_t*)malloc(aRandomSize*sizeof(uint8_t)))) + { + LOGD("## setRandomInBuffer(): failure - alloc mem OOM"); + } + else + { + LOGD("## setRandomInBuffer(): randomSize=%ld",aRandomSize); + + srand(time(NULL)); // init seed + for(size_t i=0;i Date: Fri, 7 Oct 2016 17:35:27 +0200 Subject: [PATCH 6/9] - Add decryptMessageJni() to olm_session.cpp API - review comments header - refactor utility functions --- .../main/java/org/matrix/olm/OlmAccount.java | 10 +- .../olm-sdk/src/main/jni/olm_account.cpp | 56 +---- .../olm-sdk/src/main/jni/olm_account.h | 1 - .../olm-sdk/src/main/jni/olm_session.cpp | 232 ++++++++++++------ .../olm-sdk/src/main/jni/olm_session.h | 9 +- .../olm-sdk/src/main/jni/olm_utility.cpp | 87 ++++++- .../olm-sdk/src/main/jni/olm_utility.h | 2 + 7 files changed, 264 insertions(+), 133 deletions(-) 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 15de09c..156ec1a 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 @@ -131,7 +131,8 @@ public class OlmAccount { /** * Generate a number of new one time keys.
If total number of keys stored - * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded. + * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.
+ * The corresponding keys are retrieved by {@link #oneTimeKeys()}. * @param aNumberOfKeys number of keys to generate * @return 0 if operation succeed, -1 otherwise */ @@ -141,15 +142,16 @@ public class OlmAccount { * Get the public parts of the unpublished "one time keys" for the account.
* The returned data is a JSON-formatted object with the single property * curve25519, which is itself an object mapping key id to - * base64-encoded Curve25519 key. - * These keys must be published on the server. + * base64-encoded Curve25519 key.
* @return byte array containing the one time keys if operation succeed, null otherwise */ private native byte[] oneTimeKeysJni(); /** * Return the "one time keys" in a JSON array.
- * Public API for {@link #oneTimeKeysJni()}. + * The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}
+ * Public API for {@link #oneTimeKeysJni()}.
+ * Note: these keys are to be published on the server. * @return one time keys in JSON array format if operation succeed, null otherwise */ public JSONObject oneTimeKeys() { 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 51c0ca8..ac48fd2 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 @@ -137,7 +137,7 @@ JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_identityKeysJni(JNIE else { // identity keys allocation identityKeysLength = olm_account_identity_keys_length(accountPtr); - if(NULL == (identityKeysBytesPtr=(uint8_t *)malloc(identityKeysLength*sizeof(std::uint8_t)))) + if(NULL == (identityKeysBytesPtr=(uint8_t*)malloc(identityKeysLength*sizeof(uint8_t)))) { LOGE("## identityKeys(): failure - identity keys array OOM"); } @@ -245,7 +245,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv } /** - * Get "one time keys". + * 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 **/ @@ -372,15 +372,16 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_markOneTimeKeysAsPublished } /** - * Sign a message with the ed25519 key (fingerprint) for this account. + * Sign a message with the ed25519 key (fingerprint) for this account.
+ * The signed message is returned by the function. * @param aMessage message to sign - * @return the corresponding signed message, null otherwise + * @return the 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; + void* signedMsgPtr; size_t resultSign; jstring signedMsgRetValue = NULL; @@ -406,13 +407,13 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env // signature memory allocation signatureLength = olm_account_signature_length(accountPtr); - if(NULL == (signaturePtr=(void *)malloc(signatureLength*sizeof(void*)))) + if(NULL == (signedMsgPtr = (void*)malloc(signatureLength*sizeof(uint8_t)))) { LOGE("## signMessage(): failure - signature allocation OOM"); } else { // sign message - resultSign = olm_account_sign(accountPtr, (void*)messageToSign, messageLength, signaturePtr, signatureLength); + resultSign = olm_account_sign(accountPtr, (void*)messageToSign, messageLength, signedMsgPtr, signatureLength); if(resultSign == olm_error()) { const char *errorMsgPtr = olm_account_last_error(accountPtr); @@ -422,11 +423,11 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env { // 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 + signedMsgRetValue = env->NewStringUTF((const char*)signedMsgPtr); // UTF8 LOGD("## signMessage(): success - retCode=%ld",resultSign); } - free(signaturePtr); + free(signedMsgPtr); } // release messageToSign @@ -454,40 +455,3 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmManager_getOlmLibVersion(JNIEnv } -/** -* 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 index 8ba1633..9c32912 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 @@ -7,7 +7,6 @@ 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 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 93b0658..dae905d 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 @@ -19,7 +19,8 @@ /** -* Init memory allocation for session creation. +* Init memory allocation for a session creation.
+* Make sure releaseSessionJni() is called when one is done with the session instance. * @return valid memory allocation, NULL otherwise **/ OlmSession* initializeSessionMemory() @@ -192,6 +193,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIE jint retCode = ERROR_CODE_KO; OlmSession *sessionPtr = NULL; OlmAccount *accountPtr = NULL; + const char *messagePtr = NULL; size_t sessionResult; if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) @@ -208,7 +210,6 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIE } else { // convert message to C strings - const char *messagePtr = NULL; if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0))) { LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM"); @@ -426,11 +427,10 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromI /** - * Encrypt a message using the session. to a base64 ciphertext.
- * 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 + * Encrypt a message using the session.
+ * @param aClearMsg clear text message + * @param [out] aEncryptedMsg ciphered message + * @return ERROR_CODE_OK if encrypt operation succeed, ERROR_CODE_KO otherwise */ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg) { @@ -439,11 +439,10 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * const char *clearMsgPtr = NULL; uint8_t *randomBuffPtr = NULL; void *encryptedMsgPtr = NULL; - jclass encryptedMsgJClass; + jclass encryptedMsgJClass = 0; jfieldID encryptedMsgFieldId; jfieldID typeMsgFieldId; - if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) { LOGE("## encryptMessageJni(): failure - invalid Session ptr=NULL"); @@ -485,35 +484,38 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * // alloc buffer for encrypted message size_t clearMsgLength = env->GetStringUTFLength(aClearMsg); size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength); - if(NULL == (encryptedMsgPtr = (void*)malloc(encryptedMsgLength*sizeof(void*)))) + if(NULL == (encryptedMsgPtr = (void*)malloc(encryptedMsgLength*sizeof(uint8_t)))) { LOGE("## encryptMessageJni(): failure - random buffer OOM"); } - - size_t result = olm_encrypt(sessionPtr, - (void const *)clearMsgPtr, - clearMsgLength, - randomBuffPtr, - randomLength, - encryptedMsgPtr, - encryptedMsgLength); - if(result == olm_error()) - { - const char *errorMsgPtr = olm_session_last_error(sessionPtr); - LOGE("## encryptMessageJni(): failure - Msg=%s",errorMsgPtr); - } else - { - // update type: PRE KEY message or normal message - size_t messageType = olm_encrypt_message_type(sessionPtr); - env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType); + { // encrypt message + size_t result = olm_encrypt(sessionPtr, + (void const *)clearMsgPtr, + clearMsgLength, + randomBuffPtr, + randomLength, + encryptedMsgPtr, + encryptedMsgLength); + if(result == olm_error()) + { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## encryptMessageJni(): failure - Msg=%s",errorMsgPtr); + } + else + { + // update message type: PRE KEY or normal + size_t messageType = olm_encrypt_message_type(sessionPtr); + env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType); - // update message - jstring encryptedStr = env->NewStringUTF((const char*)encryptedMsgPtr); - env->SetObjectField(aEncryptedMsg, encryptedMsgFieldId, (jobject)encryptedStr); + // update message: encryptedMsgPtr => encryptedJstring + jstring encryptedJstring = env->NewStringUTF((const char*)encryptedMsgPtr); + env->SetObjectField(aEncryptedMsg, encryptedMsgFieldId, (jobject)encryptedJstring); + // TODO mem leak: check if free(encryptedMsgPtr); does not interfer with line above - retCode = ERROR_CODE_OK; - LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%s", result, messageType, (const char*)encryptedMsgPtr); + retCode = ERROR_CODE_OK; + LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%s", result, messageType, (const char*)encryptedMsgPtr); + } } } } @@ -529,9 +531,134 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * free(randomBuffPtr); } + if(NULL != encryptedMsgPtr) + { + free(encryptedMsgPtr); + } + return retCode; } + +/** + * Decrypt a message using the session. to a base64 ciphertext.
+ * @param aEncryptedMsg message to decrypt + * @return decrypted message if operation succeed, null otherwise + */ +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEnv *env, jobject thiz, jobject aEncryptedMsg) +{ + jstring decryptedMsgRetValue = 0; + jclass encryptedMsgJclass = 0; + jstring encryptedMsgJstring = 0; // <= obtained from encryptedMsgFieldId + // field IDs + jfieldID encryptedMsgFieldId; + jfieldID typeMsgFieldId; + // ptrs + OlmSession *sessionPtr = NULL; + const char *encryptedMsgPtr = NULL; // <= obtained from encryptedMsgJstring + void *decryptedMsgPtr = NULL; + char *tempEncryptedPtr = NULL; + + + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) + { + LOGE("## decryptMessageJni(): failure - invalid Session ptr=NULL"); + } + else if(0 == aEncryptedMsg) + { + LOGE("## decryptMessageJni(): failure - invalid clear message"); + } + else if(0 == (encryptedMsgJclass = env->GetObjectClass(aEncryptedMsg))) + { + LOGE("## decryptMessageJni(): failure - unable to get crypted message class"); + } + else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mCipherText","Ljava/lang/String;"))) + { + LOGE("## decryptMessageJni(): failure - unable to get message field"); + } + else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mType","I"))) + { + LOGE("## decryptMessageJni(): failure - unable to get message type field"); + } + else if(0 == (encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId))) + { + LOGE("## decryptMessageJni(): failure - JNI encrypted object "); + } + else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0))) + { + LOGE("## decryptMessageJni(): failure - encrypted message JNI allocation OOM"); + } + else + { + // get message type + jlong encryptedMsgType = env->GetLongField(aEncryptedMsg, typeMsgFieldId); + // get encrypted message length + size_t encryptedMsgLength = env->GetStringUTFLength(encryptedMsgJstring); + + // create a dedicated temp buffer to be used in next Olm API calls + tempEncryptedPtr = (char*)malloc(encryptedMsgLength*sizeof(uint8_t)); + memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); + LOGD("## decryptMessageJni(): encryptedMsgType=%lld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr); + + // get max plaintext length + size_t maxPlaintextLength = olm_decrypt_max_plaintext_length(sessionPtr, + encryptedMsgType, + (void*)tempEncryptedPtr, + encryptedMsgLength); + // Note: tempEncryptedPtr was destroyed by olm_decrypt_max_plaintext_length() + + if(maxPlaintextLength == olm_error()) + { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## decryptMessageJni(): failure - olm_decrypt_max_plaintext_length Msg=%s",errorMsgPtr); + } + else + { + // allocate output decrypted message + decryptedMsgPtr = (void*)malloc(maxPlaintextLength*sizeof(uint8_t)); + + // decrypt but before reload encrypted buffer (previous one was destroyed) + memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); + size_t plaintextLength = olm_decrypt(sessionPtr, + encryptedMsgType, + (void*)encryptedMsgPtr, + encryptedMsgLength, + (void*)decryptedMsgPtr, + maxPlaintextLength); + if(plaintextLength == olm_error()) + { + const char *errorMsgPtr = olm_session_last_error(sessionPtr); + LOGE("## decryptMessageJni(): failure - olm_decrypt Msg=%s",errorMsgPtr); + } + else + { + decryptedMsgRetValue = env->NewStringUTF((const char*)decryptedMsgPtr); + } + } + } + + // free alloc + if(NULL != encryptedMsgPtr) + { + env->ReleaseStringUTFChars(encryptedMsgJstring, encryptedMsgPtr); + } + + if(NULL != tempEncryptedPtr) + { + free(tempEncryptedPtr); + } + + if(NULL != decryptedMsgPtr) + { + free(decryptedMsgPtr); + } + + return decryptedMsgRetValue; +} + + + + /** * Get the session identifier for this session. * @return the session identifier if operation succeed, null otherwise @@ -549,7 +676,7 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni { LOGE("## getSessionIdentifierJni(): failure - invalid Session ptr=NULL"); } - else if(NULL == (sessionIdPtr = (void*)malloc(lengthSessId*sizeof(void*)))) + else if(NULL == (sessionIdPtr = (void*)malloc(lengthSessId*sizeof(uint8_t)))) { LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM"); } @@ -571,42 +698,3 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni 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 index 8e162b0..b04e71e 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h @@ -7,8 +7,6 @@ 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); @@ -24,15 +22,12 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKe 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); +// encrypt/decrypt JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg); +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEnv *env, jobject thiz, jobject aEncryptedMsg); JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz); - -// signing - - - #ifdef __cplusplus } #endif diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp index 9abd228..4f96e10 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp @@ -50,11 +50,92 @@ bool setRandomInBuffer(uint8_t **aBuffer2Ptr, size_t aRandomSize) { (*aBuffer2Ptr)[i] = (uint8_t)(rand()%ACCOUNT_CREATION_RANDOM_MODULO); - // TODO debug purpose - remove asap - LOGD("## setRandomInBuffer(): randomBuffPtr[%ld]=%d",i, (*aBuffer2Ptr)[i]); + // debug purpose + //LOGD("## setRandomInBuffer(): randomBuffPtr[%ld]=%d",i, (*aBuffer2Ptr)[i]); } retCode = true; } return retCode; -} \ No newline at end of file +} + + +/** +* Read the account instance ID of the calling object. +* @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 operation succeed, -1 if instance ID was not found. +**/ +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; +} + +/** +* Read the account instance ID of the calling object (aJavaObject).
+* @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_utility.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h index bf29eed..6683c68 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h @@ -7,6 +7,8 @@ extern "C" { #endif bool setRandomInBuffer(uint8_t **aBuffer2Ptr, size_t aRandomSize); +jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject); +jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject); #ifdef __cplusplus } From f2ca1ce304deee9e8e858ea4a71ea22ba38a342b Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Tue, 11 Oct 2016 15:53:49 +0200 Subject: [PATCH 7/9] - Add OlmSession unit test - Simplify JNI function signatures definition (Account & Session) --- .../java/org/matrix/olm/OlmAccountTest.java | 58 +++++---- .../java/org/matrix/olm/OlmSessionTest.java | 110 ++++++++++++++++++ .../main/java/org/matrix/olm/OlmAccount.java | 26 +++-- .../main/java/org/matrix/olm/OlmSession.java | 33 ++++-- .../olm-sdk/src/main/jni/olm_account.cpp | 31 ++--- .../olm-sdk/src/main/jni/olm_account.h | 3 + .../OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h | 2 + .../olm-sdk/src/main/jni/olm_session.cpp | 107 ++++++++++------- .../olm-sdk/src/main/jni/olm_session.h | 4 +- .../olm-sdk/src/main/jni/olm_utility.cpp | 2 +- 10 files changed, 268 insertions(+), 108 deletions(-) create mode 100644 java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java index f592aa4..05480fc 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java @@ -30,9 +30,6 @@ public class OlmAccountTest { private static OlmManager mOlmManager; private boolean mIsAccountCreated; - public static final String TEST_STRING = "This is a string"; - public static final long TEST_LONG = 12345678L; - @BeforeClass public static void setUpClass(){ // load native lib @@ -61,26 +58,35 @@ public class OlmAccountTest { } @Test - public void test1CreateAccount() { + public void test01CreateReleaseAccount() { + mOlmAccount = new OlmAccount(); + assertNotNull(mOlmAccount); + + mOlmAccount.releaseAccount(); + assertTrue(0 == mOlmAccount.getOlmAccountId()); + } + + @Test + public void test02CreateAccount() { mOlmAccount = new OlmAccount(); assertNotNull(mOlmAccount); } @Test - public void test2InitNewAccount() { + public void test03InitNewAccount() { assertTrue(mOlmAccount.initNewAccount()); mIsAccountCreated = true; } @Test - public void test3GetOlmAccountId() { + public void test04GetOlmAccountId() { long olmNativeInstance = mOlmAccount.getOlmAccountId(); Log.d(LOG_TAG,"## testGetOlmAccountId olmNativeInstance="+olmNativeInstance); assertTrue(0!=olmNativeInstance); } @Test - public void test4IdentityKeys() { + public void test05IdentityKeys() { JSONObject identityKeysJson = mOlmAccount.identityKeys(); assertNotNull(identityKeysJson); Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson); @@ -108,7 +114,7 @@ public class OlmAccountTest { //** ************** One time keys TESTS ************** //**************************************************** @Test - public void test5MaxOneTimeKeys() { + public void test06MaxOneTimeKeys() { long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys(); Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys); @@ -116,13 +122,13 @@ public class OlmAccountTest { } @Test - public void test6GenerateOneTimeKeys() { + public void test07GenerateOneTimeKeys() { int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER); assertTrue(0==retValue); } @Test - public void test7OneTimeKeysJsonFormat() { + public void test08OneTimeKeysJsonFormat() { int oneTimeKeysCount = 0; JSONObject generatedKeysJsonObj; JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys(); @@ -145,27 +151,31 @@ public class OlmAccountTest { } } - // TODO testRemoveOneTimeKeysForSession when session is available - /*@Test - public void testRemoveOneTimeKeysForSession() { - Log.d(LOG_TAG,"## testRemoveOneTimeKeysForSession"); - OLMSession olmSession = new OLMSession(); + @Test + public void test10RemoveOneTimeKeysForSession() { + OlmSession olmSession = new OlmSession(); + olmSession.initNewSession(); + long sessionId = olmSession.getOlmSessionId(); + assertTrue(0 != sessionId); - JSONArray keysJsonArray = mOlmAccount.removeOneTimeKeysForSession(olmSession); + int sessionRetCode = mOlmAccount.removeOneTimeKeysForSession(sessionId); + // no one time key has been use in the session, so removeOneTimeKeysForSession() returns an error + assertTrue(0 != sessionRetCode); - assertNotNull(keysJsonArray); - // TODO add extra test to test the JSON content format.. - }*/ + olmSession.releaseSession(); + sessionId = olmSession.getOlmSessionId(); + assertTrue("sessionRetCode="+sessionRetCode,0 == sessionId); + } @Test - public void test8MarkOneTimeKeysAsPublished() { + public void test11MarkOneTimeKeysAsPublished() { int retCode = mOlmAccount.markOneTimeKeysAsPublished(); // if OK => retCode=0 assertTrue(0 == retCode); } @Test - public void test9SignMessage() { + public void test12SignMessage() { String clearMsg = "String to be signed by olm"; String signedMsg = mOlmAccount.signMessage(clearMsg); assertNotNull(signedMsg); @@ -193,10 +203,8 @@ public class OlmAccountTest { int generateRetCode = account.generateOneTimeKeys(50); Log.d(LOG_TAG, "## testJni(): generateRetCode="+generateRetCode); - JSONObject onteTimeKeysKeysJson = account.oneTimeKeys(); - Log.d(LOG_TAG, "## testJni(): onteTimeKeysKeysJson="+onteTimeKeysKeysJson.toString()); - - // TODO removeOneTimeKeysForSession(session); + JSONObject oneTimeKeysKeysJson = account.oneTimeKeys(); + Log.d(LOG_TAG, "## testJni(): oneTimeKeysKeysJson="+oneTimeKeysKeysJson.toString()); int asPublishedRetCode = account.markOneTimeKeysAsPublished(); Log.d(LOG_TAG, "## testJni(): asPublishedRetCode="+asPublishedRetCode); diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java new file mode 100644 index 0000000..fb2eebc --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java @@ -0,0 +1,110 @@ +package org.matrix.olm; + +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + +import java.util.Iterator; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class OlmSessionTest { + private static final String LOG_TAG = "OlmSessionTest"; + + private static OlmManager mOlmManager; + + @BeforeClass + public static void setUpClass(){ + // load native lib + mOlmManager = new OlmManager(); + + String version = mOlmManager.getOlmLibVersion(); + assertNotNull(version); + Log.d(LOG_TAG, "## setUpClass(): lib version="+version); + } + + @Test + public void test01AliceToBob() { + String bobIdentityKey = null; + String bobOneTimeKey=null; + + // creates alice & bob accounts + OlmAccount aliceAccount = new OlmAccount(); + aliceAccount.initNewAccount(); + + OlmAccount bobAccount = new OlmAccount(); + bobAccount.initNewAccount(); + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // get bob identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + assertNotNull(bobIdentityKeysJson); + try { + bobIdentityKey = bobIdentityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY); + assertTrue(null!=bobIdentityKey); + } catch (JSONException e) { + assertTrue("Exception MSg="+e.getMessage(), false); + } + + // get bob one time keys + assertTrue(0==bobAccount.generateOneTimeKeys(5)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + assertNotNull(bobOneTimeKeysJsonObj); + try { + JSONObject generatedKeys = bobOneTimeKeysJsonObj.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY); + assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", generatedKeys); + + // test the count of the generated one time keys: + Iterator generatedKeysIt = generatedKeys.keys(); + if(generatedKeysIt.hasNext()) { + bobOneTimeKey = generatedKeys.getString(generatedKeysIt.next()); + } + assertNotNull(bobOneTimeKey); + } catch (JSONException e) { + assertTrue("Exception MSg="+e.getMessage(), false); + } + + // CREATE ALICE SESSION + OlmSession aliceSession = new OlmSession(); + aliceSession.initNewSession(); + assertTrue(0!=aliceSession.getOlmSessionId()); + + // CREATE ALICE OUTBOUND SESSION and encrypt message to bob + assertNotNull(aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey)); + String clearMsg = "Heloo bob , this is alice!"; + OlmMessage encryptedMsgToBob = aliceSession.encryptMessage(clearMsg); + assertNotNull(encryptedMsgToBob); + Log.d(LOG_TAG,"## test01AliceToBob(): encryptedMsg="+encryptedMsgToBob.mCipherText); + + // CREATE BOB INBOUND SESSION and decrypt message from alice + OlmSession bobSession = new OlmSession(); + bobSession.initNewSession(); + assertTrue(0!=bobSession.getOlmSessionId()); + assertNotNull(bobSession.initInboundSessionWithAccount(bobAccount, encryptedMsgToBob.mCipherText)); + String decryptedMsg = bobSession.decryptMessage(encryptedMsgToBob); + assertNotNull(decryptedMsg); + + // MESSAGE COMPARISON: decrypted vs encrypted + assertTrue(clearMsg.equals(decryptedMsg)); + + // clean objects.. + assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId())); + bobAccount.releaseAccount(); + aliceAccount.releaseAccount(); + bobSession.releaseSession(); + aliceSession.releaseSession(); + } +} 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 156ec1a..e9eddc7 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 @@ -29,16 +29,12 @@ public class OlmAccount { public static final String JSON_KEY_IDENTITY_KEY = "curve25519"; public static final String JSON_KEY_FINGER_PRINT_KEY = "ed25519"; - /** instance unique identifier, used in JNI to match the corresponding native class **/ - private int mJavaInstanceId; - /** account raw pointer value (OlmAccount*) returned by JNI. * this value identifies uniquely the native account instance. */ private long mNativeOlmAccountId; public OlmAccount() { - mJavaInstanceId = hashCode(); //initNewAccount(); } @@ -101,8 +97,13 @@ public class OlmAccount { /** * Return the identity keys (identity & fingerprint keys) in a JSON array.
- * Public API for {@link #identityKeysJni()}. - * @return identity keys in JSON array format if operation succeed, null otherwise + * Public API for {@link #identityKeysJni()}.
+ * Ex: + * { + * "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg", + * "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I" + * } + * @return identity keys in JSON array if operation succeed, null otherwise */ public JSONObject identityKeys() { JSONObject identityKeysJsonObj = null; @@ -150,6 +151,14 @@ public class OlmAccount { /** * Return the "one time keys" in a JSON array.
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}
+ * Ex: + * { "curve25519": + * { + * "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg", + * "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8", + * "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA", + * } + * }
* Public API for {@link #oneTimeKeysJni()}.
* Note: these keys are to be published on the server. * @return one time keys in JSON array format if operation succeed, null otherwise @@ -192,9 +201,4 @@ public class OlmAccount { * @return the signed message if operation succeed, null otherwise */ public native String signMessage(String aMessage); - - @Override - public String toString() { - return super.toString(); - } } 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 24a4215..0c2888c 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 @@ -19,11 +19,9 @@ package org.matrix.olm; import android.text.TextUtils; import android.util.Log; -import org.json.JSONException; -import org.json.JSONObject; - public class OlmSession { private static final String LOG_TAG = "OlmSession"; + /** session raw pointer value (OlmSession*) returned by JNI. * this value uniquely identifies the native session instance. **/ @@ -44,6 +42,14 @@ public class OlmSession { return mNativeOlmSessionId; } + /** + * Getter on the session ID. + * @return native session ID + */ + public OlmAccount getOlmAccountId(){ + return mOlmAccount; + } + /** * Destroy the corresponding OLM session native object.
* This method must ALWAYS be called when this JAVA instance @@ -105,8 +111,9 @@ public class OlmSession { // set the account of this session mOlmAccount = aAccount; - int retCode = initOutboundSessionJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aTheirOneTimeKey); - retObj = this; + if(0 == initOutboundSessionJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aTheirOneTimeKey)) { + retObj = this; + } } return retObj; @@ -121,7 +128,7 @@ public class OlmSession { * Public API for {@link #initInboundSessionJni(long, String)}. * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). * @param aAccount the account to associate with this session - * @param aOneTimeKeyMsg PRE KEY message TODO TBC + * @param aOneTimeKeyMsg PRE KEY message * @return this if operation succeed, null otherwise */ public OlmSession initInboundSessionWithAccount(OlmAccount aAccount, String aOneTimeKeyMsg) { @@ -227,6 +234,7 @@ public class OlmSession { /** * Encrypt a message using the session.
+ * The encrypted message is returned in a OlmMessage object. * Public API for {@link #encryptMessageJni(String, OlmMessage)}. * @param aClearMsg message to encrypted * @return the encrypted message if operation succeed, null otherwise @@ -243,11 +251,12 @@ public class OlmSession { private native int encryptMessageJni(String aClearMsg, OlmMessage aEncryptedMsg); - - @Override - public String toString() { - return super.toString(); - } - + /** + * Decrypt a message using the session.
+ * The encrypted message is given as a OlmMessage object. + * @param aEncryptedMsg message to decrypt + * @return the decrypted message if operation succeed, null otherwise + */ + public native String decryptMessage(OlmMessage aEncryptedMsg); } 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 ac48fd2..e663fc9 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 @@ -44,7 +44,7 @@ OlmAccount* 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) +JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz) { OlmAccount* accountPtr = NULL; @@ -55,8 +55,11 @@ JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv * LOGE("## releaseAccountJni(): failure - invalid Account ptr=NULL"); } else - { // even if free(NULL) does not crash, a test is performed for debug purpose + { + olm_clear_account(accountPtr); + LOGD("## releaseAccountJni(): IN"); + // even if free(NULL) does not crash, logs are performed for debug purpose free(accountPtr); LOGD("## releaseAccountJni(): OUT"); } @@ -68,7 +71,7 @@ JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv * * 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) +JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(initNewAccountJni)(JNIEnv *env, jobject thiz) { OlmAccount *accountPtr = NULL; uint8_t *randomBuffPtr = NULL; @@ -120,7 +123,7 @@ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv * 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) +JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz) { OlmAccount* accountPtr = NULL; size_t identityKeysLength; @@ -175,7 +178,7 @@ JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_identityKeysJni(JNIE * 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) +JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeys)(JNIEnv *env, jobject thiz) { OlmAccount* accountPtr = NULL; size_t maxKeys = -1; @@ -198,7 +201,7 @@ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_maxOneTimeKeys(JNIEnv *en * @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) +JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeys)(JNIEnv *env, jobject thiz, jint aNumberOfKeys) { OlmAccount *accountPtr = NULL; uint8_t *randomBufferPtr = NULL; @@ -206,7 +209,6 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv size_t randomLength; size_t result; - LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys); if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz))) { @@ -222,7 +224,10 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv LOGE("## generateOneTimeKeys(): failure - random buffer init"); } else - { // retrieve key pairs in keysBytesPtr + { + LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys); + + // retrieve key pairs in keysBytesPtr result = olm_account_generate_one_time_keys(accountPtr, aNumberOfKeys, (void*)randomBufferPtr, randomLength); if(result == olm_error()) { const char *errorMsgPtr = olm_account_last_error(accountPtr); @@ -249,7 +254,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv * 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) +JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz) { OlmAccount* accountPtr = NULL; size_t keysLength; @@ -303,7 +308,7 @@ JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_oneTimeKeysJni(JNIEn * @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) +JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSession)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId) { jint retCode = ERROR_CODE_KO; OlmAccount* accountPtr = NULL; @@ -342,7 +347,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_removeOneTimeKeysForSessio * 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) +JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublished)(JNIEnv *env, jobject thiz) { jint retCode = ERROR_CODE_OK; OlmAccount* accountPtr = NULL; @@ -377,7 +382,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_markOneTimeKeysAsPublished * @param aMessage message to sign * @return the signed message, null otherwise **/ -JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env, jobject thiz, jstring aMessage) +JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, jstring aMessage) { OlmAccount* accountPtr = NULL; size_t signatureLength; @@ -439,7 +444,7 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env } -JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmManager_getOlmLibVersion(JNIEnv* env, jobject thiz) +JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersion)(JNIEnv* env, jobject thiz) { uint8_t majorVer=0, minorVer=0, patchVer=0; jstring returnValueStr=0; 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 9c32912..33fc65c 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 @@ -3,6 +3,9 @@ #include "olm_jni.h" +#define OLM_ACCOUNT_FUNC_DEF(func_name) FUNC_DEF(OlmAccount,func_name) +#define OLM_MANAGER_FUNC_DEF(func_name) FUNC_DEF(OlmManager,func_name) + #ifdef __cplusplus extern "C" { #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 index 1e85cc1..d811c3f 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h @@ -28,6 +28,8 @@ #define LOGE(...) #endif +#define FUNC_DEF(class_name,func_name) JNICALL Java_org_matrix_olm_##class_name##_##func_name + // Error codes definition static const int ERROR_CODE_OK = 0; static const int ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS = ERROR_CODE_OK+1; 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 dae905d..0febc02 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,7 @@ OlmSession* initializeSessionMemory() return sessionPtr; } -JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv *env, jobject thiz) +JNIEXPORT void OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz) { OlmSession* sessionPtr = NULL; @@ -50,8 +50,11 @@ JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv * LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL"); } else - { // even if free(NULL) does not crash, a test is performed for debug purpose + { + olm_clear_session(sessionPtr); + LOGD("## releaseSessionJni(): IN"); + // even if free(NULL) does not crash, logs are performed for debug purpose free(sessionPtr); LOGD("## releaseSessionJni(): OUT"); } @@ -63,7 +66,7 @@ JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv * * 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) +JNIEXPORT jlong OLM_SESSION_FUNC_DEF(initNewSessionJni)(JNIEnv *env, jobject thiz) { OlmSession* sessionPtr = NULL; @@ -92,7 +95,7 @@ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmSession_initNewSessionJni(JNIEnv * @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) +JNIEXPORT jint OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey) { jint retCode = ERROR_CODE_KO; OlmSession* sessionPtr = NULL; @@ -117,7 +120,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI else { // allocate random buffer size_t randomSize = olm_create_outbound_session_random_length(sessionPtr); - if(false == setRandomInBuffer(&randomBuffPtr, randomSize)) + if((0!=randomSize) && (false == setRandomInBuffer(&randomBuffPtr, randomSize))) { LOGE("## initOutboundSessionJni(): failure - random buffer init"); } @@ -188,7 +191,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI * @param aOneTimeKeyMsg PRE_KEY message * @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) +JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg) { jint retCode = ERROR_CODE_KO; OlmSession *sessionPtr = NULL; @@ -206,7 +209,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIE } else if(0==aOneTimeKeyMsg) { - LOGE("## initOutboundSessionJni(): failure - invalid message"); + LOGE("## initInboundSessionJni(): failure - invalid message"); } else { // convert message to C strings @@ -217,7 +220,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIE else { int messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); - LOGD("## initInboundSessionJni(): message=%s messageLength=%d",messagePtr,messageLength); + LOGD("## initInboundSessionJni(): messageLength=%d message=%s", messageLength, messagePtr); sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength); if(sessionResult == olm_error()) { @@ -245,7 +248,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIE * @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) +JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg) { jint retCode = ERROR_CODE_KO; OlmSession *sessionPtr = NULL; @@ -268,7 +271,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKe } else if(0==aOneTimeKeyMsg) { - LOGE("## initOutboundSessionJni(): failure - invalid one time key message"); + LOGE("## initInboundSessionJni(): failure - invalid one time key message"); } else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0))) { @@ -316,7 +319,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKe * @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) +JNIEXPORT jint OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobject thiz, jstring aOneTimeKeyMsg) { jint retCode = ERROR_CODE_KO; OlmSession *sessionPtr = NULL; @@ -367,7 +370,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(J * @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) +JNIEXPORT jint JNICALL OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg) { jint retCode = ERROR_CODE_KO; OlmSession *sessionPtr = NULL; @@ -432,7 +435,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromI * @param [out] aEncryptedMsg ciphered message * @return ERROR_CODE_OK if encrypt operation succeed, ERROR_CODE_KO otherwise */ -JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg) +JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg) { jint retCode = ERROR_CODE_KO; OlmSession *sessionPtr = NULL; @@ -467,15 +470,21 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * { LOGE("## encryptMessageJni(): failure - unable to get message field"); } - else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","I"))) + else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","J"))) { LOGE("## encryptMessageJni(): failure - unable to get message type field"); } else { + // get message type + size_t messageType = olm_encrypt_message_type(sessionPtr); + // compute random buffer + // Note: olm_encrypt_random_length() can return 0, which means + // it just does not need new random data to encrypt a new message size_t randomLength = olm_encrypt_random_length(sessionPtr); - if(false == setRandomInBuffer(&randomBuffPtr, randomLength)) + LOGD("## encryptMessageJni(): messageType=%lu randomLength=%lu",messageType,randomLength); + if( (0!=randomLength) && (false == setRandomInBuffer(&randomBuffPtr, randomLength))) { LOGE("## encryptMessageJni(): failure - random buffer init"); } @@ -486,10 +495,16 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength); if(NULL == (encryptedMsgPtr = (void*)malloc(encryptedMsgLength*sizeof(uint8_t)))) { - LOGE("## encryptMessageJni(): failure - random buffer OOM"); + LOGE("## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM"); } else - { // encrypt message + { + if(0==randomLength) + { + LOGW("## encryptMessageJni(): random buffer is not required"); + } + + // encrypt message size_t result = olm_encrypt(sessionPtr, (void const *)clearMsgPtr, clearMsgLength, @@ -505,13 +520,11 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * else { // update message type: PRE KEY or normal - size_t messageType = olm_encrypt_message_type(sessionPtr); env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType); // update message: encryptedMsgPtr => encryptedJstring jstring encryptedJstring = env->NewStringUTF((const char*)encryptedMsgPtr); env->SetObjectField(aEncryptedMsg, encryptedMsgFieldId, (jobject)encryptedJstring); - // TODO mem leak: check if free(encryptedMsgPtr); does not interfer with line above retCode = ERROR_CODE_OK; LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%s", result, messageType, (const char*)encryptedMsgPtr); @@ -541,11 +554,11 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv * /** - * Decrypt a message using the session. to a base64 ciphertext.
+ * Decrypt a message using the session.
* @param aEncryptedMsg message to decrypt * @return decrypted message if operation succeed, null otherwise */ -JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEnv *env, jobject thiz, jobject aEncryptedMsg) +JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg) { jstring decryptedMsgRetValue = 0; jclass encryptedMsgJclass = 0; @@ -556,37 +569,37 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEn // ptrs OlmSession *sessionPtr = NULL; const char *encryptedMsgPtr = NULL; // <= obtained from encryptedMsgJstring - void *decryptedMsgPtr = NULL; + void *plainTextMsgPtr = NULL; char *tempEncryptedPtr = NULL; if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) { - LOGE("## decryptMessageJni(): failure - invalid Session ptr=NULL"); + LOGE("## decryptMessage(): failure - invalid Session ptr=NULL"); } else if(0 == aEncryptedMsg) { - LOGE("## decryptMessageJni(): failure - invalid clear message"); + LOGE("## decryptMessage(): failure - invalid clear message"); } else if(0 == (encryptedMsgJclass = env->GetObjectClass(aEncryptedMsg))) { - LOGE("## decryptMessageJni(): failure - unable to get crypted message class"); + LOGE("## decryptMessage(): failure - unable to get crypted message class"); } else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mCipherText","Ljava/lang/String;"))) { - LOGE("## decryptMessageJni(): failure - unable to get message field"); + LOGE("## decryptMessage(): failure - unable to get message field"); } - else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mType","I"))) + else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mType","J"))) { - LOGE("## decryptMessageJni(): failure - unable to get message type field"); + LOGE("## decryptMessage(): failure - unable to get message type field"); } else if(0 == (encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId))) { - LOGE("## decryptMessageJni(): failure - JNI encrypted object "); + LOGE("## decryptMessage(): failure - JNI encrypted object "); } else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0))) { - LOGE("## decryptMessageJni(): failure - encrypted message JNI allocation OOM"); + LOGE("## decryptMessage(): failure - encrypted message JNI allocation OOM"); } else { @@ -596,43 +609,47 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEn size_t encryptedMsgLength = env->GetStringUTFLength(encryptedMsgJstring); // create a dedicated temp buffer to be used in next Olm API calls - tempEncryptedPtr = (char*)malloc(encryptedMsgLength*sizeof(uint8_t)); + tempEncryptedPtr = static_cast(malloc(encryptedMsgLength*sizeof(uint8_t))); memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); LOGD("## decryptMessageJni(): encryptedMsgType=%lld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr); // get max plaintext length - size_t maxPlaintextLength = olm_decrypt_max_plaintext_length(sessionPtr, + size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr, encryptedMsgType, - (void*)tempEncryptedPtr, + static_cast(tempEncryptedPtr), encryptedMsgLength); - // Note: tempEncryptedPtr was destroyed by olm_decrypt_max_plaintext_length() + // Note: tempEncryptedPtr is destroyed by olm_decrypt_max_plaintext_length() - if(maxPlaintextLength == olm_error()) + if(maxPlainTextLength == olm_error()) { const char *errorMsgPtr = olm_session_last_error(sessionPtr); LOGE("## decryptMessageJni(): failure - olm_decrypt_max_plaintext_length Msg=%s",errorMsgPtr); } else { - // allocate output decrypted message - decryptedMsgPtr = (void*)malloc(maxPlaintextLength*sizeof(uint8_t)); + LOGD("## decryptMessage(): maxPlaintextLength=%lu",maxPlainTextLength); - // decrypt but before reload encrypted buffer (previous one was destroyed) + // allocate output decrypted message + plainTextMsgPtr = static_cast(malloc(maxPlainTextLength*sizeof(uint8_t))); + + // decrypt, but before reload encrypted buffer (previous one was destroyed) memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); size_t plaintextLength = olm_decrypt(sessionPtr, encryptedMsgType, (void*)encryptedMsgPtr, encryptedMsgLength, - (void*)decryptedMsgPtr, - maxPlaintextLength); + (void*)plainTextMsgPtr, + maxPlainTextLength); if(plaintextLength == olm_error()) { const char *errorMsgPtr = olm_session_last_error(sessionPtr); - LOGE("## decryptMessageJni(): failure - olm_decrypt Msg=%s",errorMsgPtr); + LOGE("## decryptMessage(): failure - olm_decrypt Msg=%s",errorMsgPtr); } else { - decryptedMsgRetValue = env->NewStringUTF((const char*)decryptedMsgPtr); + (static_cast(plainTextMsgPtr))[plaintextLength] = static_cast('\0'); + LOGD("## decryptMessage(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, static_cast(plainTextMsgPtr)); + decryptedMsgRetValue = env->NewStringUTF(static_cast(plainTextMsgPtr)); } } } @@ -648,9 +665,9 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEn free(tempEncryptedPtr); } - if(NULL != decryptedMsgPtr) + if(NULL != plainTextMsgPtr) { - free(decryptedMsgPtr); + free(plainTextMsgPtr); } return decryptedMsgRetValue; @@ -663,7 +680,7 @@ JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEn * 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) +JNIEXPORT jstring OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, jobject thiz) { OlmSession *sessionPtr = NULL; void *sessionIdPtr = NULL; 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 index b04e71e..9b3c30f 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h @@ -3,6 +3,8 @@ #include "olm_jni.h" +#define OLM_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmSession,func_name) + #ifdef __cplusplus extern "C" { #endif @@ -24,7 +26,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromI // encrypt/decrypt JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg); -JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessageJni(JNIEnv *env, jobject thiz, jobject aEncryptedMsg); +JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessage(JNIEnv *env, jobject thiz, jobject aEncryptedMsg); JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz); diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp index 4f96e10..99df7f5 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp @@ -118,7 +118,7 @@ jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject) { if(0 != (instanceIdField=aJniEnv->GetFieldID(loaderClass, "mNativeOlmSessionId", "J"))) { - instanceId = aJniEnv->GetIntField(aJavaObject, instanceIdField); + instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField); aJniEnv->DeleteLocalRef(loaderClass); } else From 1679c4513f8e4965ad44b4fdad22cfda609e16fc Mon Sep 17 00:00:00 2001 From: pedroGitt Date: Wed, 12 Oct 2016 19:04:50 +0200 Subject: [PATCH 8/9] Temp commit: debug in progress --- .../java/org/matrix/olm/OlmSessionTest.java | 136 +++++++++++++++++- .../olm-sdk/src/main/jni/olm_session.cpp | 27 ++-- .../olm-sdk/src/main/jni/olm_utility.cpp | 2 +- 3 files changed, 150 insertions(+), 15 deletions(-) diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java index fb2eebc..93e70c5 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java @@ -33,8 +33,18 @@ public class OlmSessionTest { Log.d(LOG_TAG, "## setUpClass(): lib version="+version); } + /** + * Basic test: + * - alice creates an account + * - bob creates an account + * - alice creates an outbound session with bob (bobIdentityKey & bobOneTimeKey) + * - alice encrypts a message with its session + * - bob creates an inbound session based on alice's encrypted message + * - bob decrypts the encrypted message with its session + */ @Test public void test01AliceToBob() { + final int ONE_TIME_KEYS_NUMBER = 5; String bobIdentityKey = null; String bobOneTimeKey=null; @@ -60,14 +70,13 @@ public class OlmSessionTest { } // get bob one time keys - assertTrue(0==bobAccount.generateOneTimeKeys(5)); + assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); assertNotNull(bobOneTimeKeysJsonObj); try { JSONObject generatedKeys = bobOneTimeKeysJsonObj.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY); assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", generatedKeys); - // test the count of the generated one time keys: Iterator generatedKeysIt = generatedKeys.keys(); if(generatedKeysIt.hasNext()) { bobOneTimeKey = generatedKeys.getString(generatedKeysIt.next()); @@ -102,9 +111,132 @@ public class OlmSessionTest { // clean objects.. assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId())); + // release accounts + bobAccount.releaseAccount(); + aliceAccount.releaseAccount(); + // release sessions + bobSession.releaseSession(); + aliceSession.releaseSession(); + } + + + /** + * Same as test01AliceToBob but with bob who's encrypting messages + * to alice and alice decrypt them.
+ * - alice creates an account + * - bob creates an account + * - alice creates an outbound session with bob (bobIdentityKey & bobOneTimeKey) + * - alice encrypts a message with its own session + * - bob creates an inbound session based on alice's encrypted message + * - bob decrypts the encrypted message with its own session + * - bob encrypts messages with its own session + * - alice decrypts bob's messages with its own message + */ + @Test + public void test02AliceToBobBackAndForth() { + final int ONE_TIME_KEYS_NUMBER = 1; + String bobIdentityKey = null; + String bobOneTimeKey=null; + + // creates alice & bob accounts + OlmAccount aliceAccount = new OlmAccount(); + aliceAccount.initNewAccount(); + + OlmAccount bobAccount = new OlmAccount(); + bobAccount.initNewAccount(); + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // get bob identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + assertNotNull(bobIdentityKeysJson); + try { + bobIdentityKey = bobIdentityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY); + assertTrue(null!=bobIdentityKey); + } catch (JSONException e) { + assertTrue("Exception MSg="+e.getMessage(), false); + } + + // get bob one time keys + assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + assertNotNull(bobOneTimeKeysJsonObj); + try { + JSONObject generatedKeys = bobOneTimeKeysJsonObj.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY); + assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", generatedKeys); + + Iterator generatedKeysIt = generatedKeys.keys(); + if(generatedKeysIt.hasNext()) { + bobOneTimeKey = generatedKeys.getString(generatedKeysIt.next()); + } + assertNotNull(bobOneTimeKey); + } catch (JSONException e) { + assertTrue("Exception MSg="+e.getMessage(), false); + } + + // CREATE ALICE SESSION + OlmSession aliceSession = new OlmSession(); + aliceSession.initNewSession(); + assertTrue(0!=aliceSession.getOlmSessionId()); + + // CREATE ALICE OUTBOUND SESSION and encrypt message to bob + assertNotNull(aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey)); + String helloClearMsg = "Hello I'm Alice!"; + String goodbyeClearMsg = "Goodbye Alice"; + OlmMessage encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg); + //OlmMessage encryptedAliceToBobMsg2 = aliceSession.encryptMessage(goodbyeClearMsg); + assertNotNull(encryptedAliceToBobMsg1); + //assertNotNull(encryptedAliceToBobMsg2); + Log.d(LOG_TAG,"## test02AliceToBobBackAndForth(): encryptedMsg="+encryptedAliceToBobMsg1.mCipherText); + + // CREATE BOB INBOUND SESSION and decrypt message from alice + OlmSession bobSession = new OlmSession(); + bobSession.initNewSession(); + assertTrue(0!=bobSession.getOlmSessionId()); + assertNotNull(bobSession.initInboundSessionWithAccount(bobAccount, encryptedAliceToBobMsg1.mCipherText)); + + // DECRYPT MESSAGE FROM ALICE + String decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1); + //String decryptedMsg02 = bobSession.decryptMessage(encryptedAliceToBobMsg2); + assertNotNull(decryptedMsg01); + //assertNotNull(decryptedMsg02); + + // MESSAGE COMPARISON: decrypted vs encrypted + assertTrue(helloClearMsg.equals(decryptedMsg01)); + //assertTrue(goodbyeClearMsg.equals(decryptedMsg02)); + + assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId())); + + // BACK/FORTH MESSAGE COMPARISON + String clearMsg1 = "Hello I'm Bob!"; + String clearMsg2 = "Isn't life grand?"; + String clearMsg3 = "Let's go to the opera."; + + OlmMessage encryptedMsg1 = bobSession.encryptMessage(clearMsg1); + assertNotNull(encryptedMsg1); + /*OlmMessage encryptedMsg2 = bobSession.encryptMessage(clearMsg2); + assertNotNull(encryptedMsg2); + OlmMessage encryptedMsg3 = bobSession.encryptMessage(clearMsg3); + assertNotNull(encryptedMsg3);*/ + + String decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1); + //assertNotNull(decryptedMsg1); + /*String decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2); + //assertNotNull(decryptedMsg2); + String decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3); + //assertNotNull(decryptedMsg3);*/ + + /*assertTrue(clearMsg1.equals(decryptedMsg1)); +/* assertTrue(clearMsg2.equals(decryptedMsg2)); + assertTrue(clearMsg3.equals(decryptedMsg3));*/ + + // clean objects.. bobAccount.releaseAccount(); aliceAccount.releaseAccount(); bobSession.releaseSession(); aliceSession.releaseSession(); } + } 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 0febc02..23cbaee 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 @@ -446,6 +446,8 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz jfieldID encryptedMsgFieldId; jfieldID typeMsgFieldId; + LOGD("## encryptMessageJni(): IN "); + if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) { LOGE("## encryptMessageJni(): failure - invalid Session ptr=NULL"); @@ -572,34 +574,35 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz void *plainTextMsgPtr = NULL; char *tempEncryptedPtr = NULL; + LOGD("## decryptMessage(): IN "); if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz))) { - LOGE("## decryptMessage(): failure - invalid Session ptr=NULL"); + LOGE("## decryptMessage(): failure - invalid Session ptr=NULL"); } else if(0 == aEncryptedMsg) { - LOGE("## decryptMessage(): failure - invalid clear message"); + LOGE("## decryptMessage(): failure - invalid clear message"); } else if(0 == (encryptedMsgJclass = env->GetObjectClass(aEncryptedMsg))) { - LOGE("## decryptMessage(): failure - unable to get crypted message class"); + LOGE("## decryptMessage(): failure - unable to get crypted message class"); } else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mCipherText","Ljava/lang/String;"))) { - LOGE("## decryptMessage(): failure - unable to get message field"); + LOGE("## decryptMessage(): failure - unable to get message field"); } else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mType","J"))) { - LOGE("## decryptMessage(): failure - unable to get message type field"); + LOGE("## decryptMessage(): failure - unable to get message type field"); } else if(0 == (encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId))) { - LOGE("## decryptMessage(): failure - JNI encrypted object "); + LOGE("## decryptMessage(): failure - JNI encrypted object "); } else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0))) { - LOGE("## decryptMessage(): failure - encrypted message JNI allocation OOM"); + LOGE("## decryptMessage(): failure - encrypted message JNI allocation OOM"); } else { @@ -611,7 +614,7 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz // create a dedicated temp buffer to be used in next Olm API calls tempEncryptedPtr = static_cast(malloc(encryptedMsgLength*sizeof(uint8_t))); memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); - LOGD("## decryptMessageJni(): encryptedMsgType=%lld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr); + LOGD("## decryptMessageJni(): encryptedMsgType=%lld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr); // get max plaintext length size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr, @@ -623,11 +626,11 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz if(maxPlainTextLength == olm_error()) { const char *errorMsgPtr = olm_session_last_error(sessionPtr); - LOGE("## decryptMessageJni(): failure - olm_decrypt_max_plaintext_length Msg=%s",errorMsgPtr); + LOGE("## decryptMessage(): failure - olm_decrypt_max_plaintext_length Msg=%s",errorMsgPtr); } else { - LOGD("## decryptMessage(): maxPlaintextLength=%lu",maxPlainTextLength); + LOGD("## decryptMessage(): maxPlaintextLength=%lu",maxPlainTextLength); // allocate output decrypted message plainTextMsgPtr = static_cast(malloc(maxPlainTextLength*sizeof(uint8_t))); @@ -643,12 +646,12 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz if(plaintextLength == olm_error()) { const char *errorMsgPtr = olm_session_last_error(sessionPtr); - LOGE("## decryptMessage(): failure - olm_decrypt Msg=%s",errorMsgPtr); + LOGE("## decryptMessage(): failure - olm_decrypt Msg=%s",errorMsgPtr); } else { (static_cast(plainTextMsgPtr))[plaintextLength] = static_cast('\0'); - LOGD("## decryptMessage(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, static_cast(plainTextMsgPtr)); + LOGD("## decryptMessage(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, static_cast(plainTextMsgPtr)); decryptedMsgRetValue = env->NewStringUTF(static_cast(plainTextMsgPtr)); } } diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp index 99df7f5..c27fe7c 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp +++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp @@ -136,6 +136,6 @@ jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject) LOGD("## getSessionInstanceId() ERROR! aJniEnv=NULL"); } - LOGD("## getSessionInstanceId() success - instanceId=%lld",instanceId); + //LOGD("## getSessionInstanceId() success - instanceId=%lld",instanceId); return instanceId; } From f88ee7677ccee62ae2ddb1d0125ec673b0b39bd7 Mon Sep 17 00:00:00 2001 From: PedroGitt Date: Thu, 13 Oct 2016 00:19:47 +0200 Subject: [PATCH 9/9] - Fix encrypt API (update lencrypted ength) - Fix warning compiler --- .../java/org/matrix/olm/OlmSessionTest.java | 59 ++++++++++++++----- .../olm-sdk/src/main/jni/olm_account.cpp | 10 ++-- .../olm-sdk/src/main/jni/olm_session.cpp | 53 ++++++++++------- 3 files changed, 80 insertions(+), 42 deletions(-) diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java index 93e70c5..56048cc 100644 --- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java @@ -184,12 +184,9 @@ public class OlmSessionTest { // CREATE ALICE OUTBOUND SESSION and encrypt message to bob assertNotNull(aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey)); String helloClearMsg = "Hello I'm Alice!"; - String goodbyeClearMsg = "Goodbye Alice"; + OlmMessage encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg); - //OlmMessage encryptedAliceToBobMsg2 = aliceSession.encryptMessage(goodbyeClearMsg); assertNotNull(encryptedAliceToBobMsg1); - //assertNotNull(encryptedAliceToBobMsg2); - Log.d(LOG_TAG,"## test02AliceToBobBackAndForth(): encryptedMsg="+encryptedAliceToBobMsg1.mCipherText); // CREATE BOB INBOUND SESSION and decrypt message from alice OlmSession bobSession = new OlmSession(); @@ -199,13 +196,10 @@ public class OlmSessionTest { // DECRYPT MESSAGE FROM ALICE String decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1); - //String decryptedMsg02 = bobSession.decryptMessage(encryptedAliceToBobMsg2); assertNotNull(decryptedMsg01); - //assertNotNull(decryptedMsg02); // MESSAGE COMPARISON: decrypted vs encrypted assertTrue(helloClearMsg.equals(decryptedMsg01)); - //assertTrue(goodbyeClearMsg.equals(decryptedMsg02)); assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId())); @@ -216,21 +210,21 @@ public class OlmSessionTest { OlmMessage encryptedMsg1 = bobSession.encryptMessage(clearMsg1); assertNotNull(encryptedMsg1); - /*OlmMessage encryptedMsg2 = bobSession.encryptMessage(clearMsg2); + OlmMessage encryptedMsg2 = bobSession.encryptMessage(clearMsg2); assertNotNull(encryptedMsg2); OlmMessage encryptedMsg3 = bobSession.encryptMessage(clearMsg3); - assertNotNull(encryptedMsg3);*/ + assertNotNull(encryptedMsg3); String decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1); - //assertNotNull(decryptedMsg1); - /*String decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2); - //assertNotNull(decryptedMsg2); + assertNotNull(decryptedMsg1); + String decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2); + assertNotNull(decryptedMsg2); String decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3); - //assertNotNull(decryptedMsg3);*/ + assertNotNull(decryptedMsg3); - /*assertTrue(clearMsg1.equals(decryptedMsg1)); -/* assertTrue(clearMsg2.equals(decryptedMsg2)); - assertTrue(clearMsg3.equals(decryptedMsg3));*/ + assertTrue(clearMsg1.equals(decryptedMsg1)); + assertTrue(clearMsg2.equals(decryptedMsg2)); + assertTrue(clearMsg3.equals(decryptedMsg3)); // clean objects.. bobAccount.releaseAccount(); @@ -239,4 +233,37 @@ public class OlmSessionTest { aliceSession.releaseSession(); } + @Test + public void test03AliceBobSessionId() { + // creates alice & bob accounts + OlmAccount aliceAccount = new OlmAccount(); + aliceAccount.initNewAccount(); + + OlmAccount bobAccount = new OlmAccount(); + bobAccount.initNewAccount(); + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // CREATE ALICE SESSION + OlmSession aliceSession = new OlmSession(); + aliceSession.initNewSession(); + assertTrue(0!=aliceSession.getOlmSessionId()); + + // CREATE BOB INBOUND SESSION and decrypt message from alice + OlmSession bobSession = new OlmSession(); + bobSession.initNewSession(); + assertTrue(0!=bobSession.getOlmSessionId()); + + String aliceSessionId = aliceSession.sessionIdentifier(); + assertNotNull(aliceSessionId); + + String bobSessionId = bobSession.sessionIdentifier(); + assertNotNull(bobSessionId); + + // must be the same for both ends of the conversation + assertTrue(aliceSessionId.equals(bobSessionId)); + } + } 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 e663fc9..2a5ab6f 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 @@ -87,7 +87,7 @@ JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(initNewAccountJni)(JNIEnv *env, jobject thi { // allocate random buffer randomSize = olm_create_account_random_length(accountPtr); - if(false == setRandomInBuffer(&randomBuffPtr, randomSize)) + if(!setRandomInBuffer(&randomBuffPtr, randomSize)) { LOGE("## initNewAccount(): failure - random buffer init"); } @@ -216,10 +216,10 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeys)(JNIEnv *env, jobject th } else { // keys memory allocation - randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, aNumberOfKeys); + randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, (size_t)aNumberOfKeys); LOGD("## generateOneTimeKeys(): randomLength=%ld", randomLength); - if(false == setRandomInBuffer(&randomBufferPtr, randomLength)) + if(!setRandomInBuffer(&randomBufferPtr, randomLength)) { LOGE("## generateOneTimeKeys(): failure - random buffer init"); } @@ -228,7 +228,7 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeys)(JNIEnv *env, jobject th LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys); // retrieve key pairs in keysBytesPtr - result = olm_account_generate_one_time_keys(accountPtr, aNumberOfKeys, (void*)randomBufferPtr, randomLength); + result = olm_account_generate_one_time_keys(accountPtr, (size_t)aNumberOfKeys, (void*)randomBufferPtr, randomLength); if(result == olm_error()) { const char *errorMsgPtr = olm_account_last_error(accountPtr); LOGE("## generateOneTimeKeys(): failure - error generating one time keys Msg=%s",errorMsgPtr); @@ -418,7 +418,7 @@ JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, j } else { // sign message - resultSign = olm_account_sign(accountPtr, (void*)messageToSign, messageLength, signedMsgPtr, signatureLength); + resultSign = olm_account_sign(accountPtr, (void*)messageToSign, (size_t)messageLength, signedMsgPtr, signatureLength); if(resultSign == olm_error()) { const char *errorMsgPtr = olm_account_last_error(accountPtr); 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 23cbaee..bdc2239 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 @@ -120,7 +120,7 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject else { // allocate random buffer size_t randomSize = olm_create_outbound_session_random_length(sessionPtr); - if((0!=randomSize) && (false == setRandomInBuffer(&randomBuffPtr, randomSize))) + if((0!=randomSize) && !setRandomInBuffer(&randomBuffPtr, randomSize)) { LOGE("## initOutboundSessionJni(): failure - random buffer init"); } @@ -136,8 +136,8 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject } else { - int theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey); - int theirOneTimeKeyLength = env->GetStringUTFLength(aTheirOneTimeKey); + size_t theirIdentityKeyLength = (size_t)env->GetStringUTFLength(aTheirIdentityKey); + size_t theirOneTimeKeyLength = (size_t)env->GetStringUTFLength(aTheirOneTimeKey); LOGD("## initOutboundSessionJni(): identityKey=%s oneTimeKey=%s",theirIdentityKeyPtr,theirOneTimeKeyPtr); sessionResult = olm_create_outbound_session(sessionPtr, @@ -219,7 +219,7 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject } else { - int messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg); LOGD("## initInboundSessionJni(): messageLength=%d message=%s", messageLength, messagePtr); sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength); @@ -283,8 +283,8 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, } else { - size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); - size_t theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey); + size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg); + size_t theirIdentityKeyLength = (size_t)env->GetStringUTFLength(aTheirIdentityKey); LOGD("## initInboundSessionFromIdKeyJni(): message=%s messageLength=%lu",messagePtr,messageLength); @@ -339,7 +339,7 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobje } else { - size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg); size_t matchResult = olm_matches_inbound_session(sessionPtr, (void*)messagePtr , messageLength); if(matchResult == olm_error()) { @@ -399,8 +399,8 @@ JNIEXPORT jint JNICALL OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(J } else { - size_t identityKeyLength = env->GetStringUTFLength(aTheirIdentityKey); - size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg); + size_t identityKeyLength = (size_t)env->GetStringUTFLength(aTheirIdentityKey); + size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg); size_t matchResult = olm_matches_inbound_session_from(sessionPtr, (void const *)theirIdentityKeyPtr, identityKeyLength, (void*)messagePtr , messageLength); if(matchResult == olm_error()) { @@ -485,15 +485,15 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz // Note: olm_encrypt_random_length() can return 0, which means // it just does not need new random data to encrypt a new message size_t randomLength = olm_encrypt_random_length(sessionPtr); - LOGD("## encryptMessageJni(): messageType=%lu randomLength=%lu",messageType,randomLength); - if( (0!=randomLength) && (false == setRandomInBuffer(&randomBuffPtr, randomLength))) + + if((0!=randomLength) && !setRandomInBuffer(&randomBuffPtr, randomLength)) { LOGE("## encryptMessageJni(): failure - random buffer init"); } else { // alloc buffer for encrypted message - size_t clearMsgLength = env->GetStringUTFLength(aClearMsg); + size_t clearMsgLength = (size_t)env->GetStringUTFLength(aClearMsg); size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength); if(NULL == (encryptedMsgPtr = (void*)malloc(encryptedMsgLength*sizeof(uint8_t)))) { @@ -506,6 +506,7 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz LOGW("## encryptMessageJni(): random buffer is not required"); } + LOGD("## encryptMessageJni(): messageType=%lu randomLength=%lu clearMsgLength=%lu encryptedMsgLength=%lu",messageType,randomLength, clearMsgLength, encryptedMsgLength); // encrypt message size_t result = olm_encrypt(sessionPtr, (void const *)clearMsgPtr, @@ -521,15 +522,19 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz } else { + // update encrypted buffer size + (static_cast(encryptedMsgPtr))[result] = static_cast('\0'); + // update message type: PRE KEY or normal env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType); // update message: encryptedMsgPtr => encryptedJstring jstring encryptedJstring = env->NewStringUTF((const char*)encryptedMsgPtr); + size_t encryptedUtfMsgLength = (size_t)env->GetStringUTFLength(encryptedJstring); env->SetObjectField(aEncryptedMsg, encryptedMsgFieldId, (jobject)encryptedJstring); retCode = ERROR_CODE_OK; - LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%s", result, messageType, (const char*)encryptedMsgPtr); + LOGD("## encryptMessageJni(): success - result=%lu Type=%lu utfLength=%lu encryptedMsg=%s", result, messageType, encryptedUtfMsgLength, (const char*)encryptedMsgPtr); } } } @@ -607,18 +612,18 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz else { // get message type - jlong encryptedMsgType = env->GetLongField(aEncryptedMsg, typeMsgFieldId); + size_t encryptedMsgType = (size_t)env->GetLongField(aEncryptedMsg, typeMsgFieldId); // get encrypted message length - size_t encryptedMsgLength = env->GetStringUTFLength(encryptedMsgJstring); + size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(encryptedMsgJstring); // create a dedicated temp buffer to be used in next Olm API calls tempEncryptedPtr = static_cast(malloc(encryptedMsgLength*sizeof(uint8_t))); memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); - LOGD("## decryptMessageJni(): encryptedMsgType=%lld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr); + LOGD("## decryptMessage(): MsgType=%ld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr); // get max plaintext length size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr, - encryptedMsgType, + static_cast(encryptedMsgType), static_cast(tempEncryptedPtr), encryptedMsgLength); // Note: tempEncryptedPtr is destroyed by olm_decrypt_max_plaintext_length() @@ -641,7 +646,7 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz encryptedMsgType, (void*)encryptedMsgPtr, encryptedMsgLength, - (void*)plainTextMsgPtr, + plainTextMsgPtr, maxPlainTextLength); if(plaintextLength == olm_error()) { @@ -650,7 +655,9 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz } else { + // update decrypted buffer size (static_cast(plainTextMsgPtr))[plaintextLength] = static_cast('\0'); + LOGD("## decryptMessage(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, static_cast(plainTextMsgPtr)); decryptedMsgRetValue = env->NewStringUTF(static_cast(plainTextMsgPtr)); } @@ -690,19 +697,19 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, job jstring returnValueStr=0; // get the size to alloc to contain the id - size_t lengthSessId = olm_session_id_length(sessionPtr); + size_t lengthSessionId = 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(uint8_t)))) + else if(NULL == (sessionIdPtr = (void*)malloc(lengthSessionId*sizeof(uint8_t)))) { LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM"); } else { - size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessId); + size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessionId); if (result == olm_error()) { const char *errorMsgPtr = olm_session_last_error(sessionPtr); @@ -710,6 +717,10 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, job } else { + // update decrypted buffer size + (static_cast(sessionIdPtr))[result] = static_cast('\0'); + + LOGD("## getSessionIdentifierJni(): success - result=%lu sessionId=%s",result, (char*)sessionIdPtr); returnValueStr = env->NewStringUTF((const char*)sessionIdPtr); } free(sessionIdPtr);