Merge remote-tracking branch 'origin/pedroc/android_e2e' into pedroc/android_e2e_dev

release-v2.2.0
pedroGitt 2016-10-13 09:36:43 +02:00
commit 502de788f4
23 changed files with 2943 additions and 0 deletions

View File

@ -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
}

View File

@ -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

160
java/android/OlmLibSdk/gradlew vendored Normal file
View File

@ -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 "$@"

90
java/android/OlmLibSdk/gradlew.bat vendored Normal file
View File

@ -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

View File

@ -0,0 +1,90 @@
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 ndkBuildNativeRelease(type: Exec, description: 'NDK building..') {
println 'ndkBuildNativeRelease starts..'
workingDir file('src/main')
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') {
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'clean'
}
/*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
}
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'
}

View File

@ -0,0 +1,220 @@
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 java.util.Iterator;
import static org.junit.Assert.assertNotNull;
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;
@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 test01CreateReleaseAccount() {
mOlmAccount = new OlmAccount();
assertNotNull(mOlmAccount);
mOlmAccount.releaseAccount();
assertTrue(0 == mOlmAccount.getOlmAccountId());
}
@Test
public void test02CreateAccount() {
mOlmAccount = new OlmAccount();
assertNotNull(mOlmAccount);
}
@Test
public void test03InitNewAccount() {
assertTrue(mOlmAccount.initNewAccount());
mIsAccountCreated = true;
}
@Test
public void test04GetOlmAccountId() {
long olmNativeInstance = mOlmAccount.getOlmAccountId();
Log.d(LOG_TAG,"## testGetOlmAccountId olmNativeInstance="+olmNativeInstance);
assertTrue(0!=olmNativeInstance);
}
@Test
public void test05IdentityKeys() {
JSONObject identityKeysJson = mOlmAccount.identityKeys();
assertNotNull(identityKeysJson);
Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson);
try {
String fingerPrintKey = identityKeysJson.getString(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
assertTrue("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);
assertTrue("identity key missing",!TextUtils.isEmpty(identityKey));
} catch (JSONException e) {
e.printStackTrace();
assertTrue("Exception MSg="+e.getMessage(), false);
}
}
//****************************************************
//** ************** One time keys TESTS **************
//****************************************************
@Test
public void test06MaxOneTimeKeys() {
long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys();
Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys);
assertTrue(maxOneTimeKeys>0);
}
@Test
public void test07GenerateOneTimeKeys() {
int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
assertTrue(0==retValue);
}
@Test
public void test08OneTimeKeysJsonFormat() {
int oneTimeKeysCount = 0;
JSONObject generatedKeysJsonObj;
JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys();
assertNotNull(oneTimeKeysJson);
try {
generatedKeysJsonObj = oneTimeKeysJson.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY);
assertTrue(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null!=generatedKeysJsonObj);
// test the count of the generated one time keys:
Iterator<String> 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);
} catch (JSONException e) {
assertTrue("Exception MSg="+e.getMessage(), false);
}
}
@Test
public void test10RemoveOneTimeKeysForSession() {
OlmSession olmSession = new OlmSession();
olmSession.initNewSession();
long sessionId = olmSession.getOlmSessionId();
assertTrue(0 != sessionId);
int sessionRetCode = mOlmAccount.removeOneTimeKeysForSession(sessionId);
// no one time key has been use in the session, so removeOneTimeKeysForSession() returns an error
assertTrue(0 != sessionRetCode);
olmSession.releaseSession();
sessionId = olmSession.getOlmSessionId();
assertTrue("sessionRetCode="+sessionRetCode,0 == sessionId);
}
@Test
public void test11MarkOneTimeKeysAsPublished() {
int retCode = mOlmAccount.markOneTimeKeysAsPublished();
// if OK => retCode=0
assertTrue(0 == retCode);
}
@Test
public void test12SignMessage() {
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 oneTimeKeysKeysJson = account.oneTimeKeys();
Log.d(LOG_TAG, "## testJni(): oneTimeKeysKeysJson="+oneTimeKeysKeysJson.toString());
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();
}
}

View File

@ -0,0 +1,269 @@
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);
}
/**
* 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;
// 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<String> 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()));
// 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.<br>
* - 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<String> 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!";
OlmMessage encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg);
assertNotNull(encryptedAliceToBobMsg1);
// 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);
assertNotNull(decryptedMsg01);
// MESSAGE COMPARISON: decrypted vs encrypted
assertTrue(helloClearMsg.equals(decryptedMsg01));
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();
}
@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));
}
}

View File

@ -0,0 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.matrix.olm">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
</application>
</manifest>

View File

@ -0,0 +1,204 @@
/*
* 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 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";
/** account raw pointer value (OlmAccount*) returned by JNI.
* this value identifies uniquely the native account instance.
*/
private long mNativeOlmAccountId;
public OlmAccount() {
//initNewAccount();
}
/**
* Getter on the account ID.
* @return native account ID
*/
public long getOlmAccountId(){
return mNativeOlmAccountId;
}
/**
* Destroy the corresponding OLM account native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #initNewAccountJni()}.
*/
private native void releaseAccountJni();
/**
* Release native account and invalid its JAVA reference counter part.<br>
* 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.<br>
* 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()}.<br>
* 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).<br>
* 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 (identity & fingerprint keys) in a JSON array.<br>
* Public API for {@link #identityKeysJni()}.<br>
* Ex:<tt>
* {
* "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg",
* "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I"
* }</tt>
* @return identity keys in JSON array 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.<br> If total number of keys stored
* by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.<br>
* The corresponding keys are retrieved by {@link #oneTimeKeys()}.
* @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.<br>
* The returned data is a JSON-formatted object with the single property
* <tt>curve25519</tt>, which is itself an object mapping key id to
* base64-encoded Curve25519 key.<br>
* @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.<br>
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}<br>
* Ex:<tt>
* { "curve25519":
* {
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg",
* "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8",
* "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA",
* }
* }</tt><br>
* Public API for {@link #oneTimeKeysJni()}.<br>
* 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() {
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);
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -0,0 +1,262 @@
/*
* 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;
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;
}
/**
* Getter on the session ID.
* @return native session ID
*/
public OlmAccount getOlmAccountId(){
return mOlmAccount;
}
/**
* Destroy the corresponding OLM session native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #initNewSessionJni()}.
*/
private native void releaseSessionJni();
/**
* Release native session and invalid its JAVA reference counter part.<br>
* 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()}.<br>
* 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.<br>
* 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.<br>
* 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;
if(0 == 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.<br>
* 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
* @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.<br>
* 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
* @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);
/**
* Get the session identifier.<br> 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.<br>
* 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);
/**
* Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.<br>
* 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 boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) {
boolean retCode = false;
if(0 == matchesInboundSessionFromIdKeyJni(aTheirIdentityKey, aOneTimeKeyMsg)){
retCode = true;
}
return retCode;
}
private native int matchesInboundSessionFromIdKeyJni(String aTheirIdentityKey, String aOneTimeKeyMsg);
/**
* Encrypt a message using the session.<br>
* 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
*/
public OlmMessage encryptMessage(String aClearMsg) {
OlmMessage encryptedMsgRetValue = new OlmMessage();
if(0 != encryptMessageJni(aClearMsg, encryptedMsgRetValue)){
encryptedMsgRetValue = null;
}
return encryptedMsgRetValue;
}
private native int encryptMessageJni(String aClearMsg, OlmMessage aEncryptedMsg);
/**
* Decrypt a message using the session.<br>
* 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);
}

View File

@ -0,0 +1,53 @@
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 \
olm_utility.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,3 @@
APP_PLATFORM := android-21
APP_ABI := arm64-v8a #armeabi-v7a armeabi x86 x86_64
APP_STL := gnustl_static

View File

@ -0,0 +1,462 @@
/*
* 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"
#include "olm_utility.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().<br>
* This method MUST be called when java counter part account instance is done.
*
*/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(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
{
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");
}
}
/**
* Initialize a new account and return it to JAVA side.<br>
* 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 OLM_ACCOUNT_FUNC_DEF(initNewAccountJni)(JNIEnv *env, jobject thiz)
{
OlmAccount *accountPtr = NULL;
uint8_t *randomBuffPtr = NULL;
size_t accountRetCode;
size_t 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(!setRandomInBuffer(&randomBuffPtr, randomSize))
{
LOGE("## initNewAccount(): failure - random buffer init");
}
else
{
// create account
accountRetCode = olm_create_account(accountPtr, (void*)randomBuffPtr, randomSize);
if(accountRetCode == olm_error()) {
const char *errorMsgPtr = olm_account_last_error(accountPtr);
LOGE("## initNewAccount(): failure - account creation failed Msg=%s", errorMsgPtr);
}
LOGD("## initNewAccount(): success - OLM account created");
LOGD("## initNewAccount(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr);
}
}
if(NULL != randomBuffPtr)
{
free(randomBuffPtr);
}
return (jlong)(intptr_t)accountPtr;
}
// *********************************************************************
// ************************* IDENTITY KEYS API *************************
// *********************************************************************
/**
* Get identity keys: Ed25519 fingerprint key and Curve25519 identity key.<br>
* The keys are returned in the byte array.
* @return a valid byte array if operation succeed, null otherwise
**/
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(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(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 OLM_ACCOUNT_FUNC_DEF(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 OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeys)(JNIEnv *env, jobject thiz, jint aNumberOfKeys)
{
OlmAccount *accountPtr = NULL;
uint8_t *randomBufferPtr = NULL;
jint retCode = ERROR_CODE_KO;
size_t randomLength;
size_t result;
if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
{
LOGE("## generateOneTimeKeys(): failure - invalid Account ptr");
}
else
{ // keys memory allocation
randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, (size_t)aNumberOfKeys);
LOGD("## generateOneTimeKeys(): randomLength=%ld", randomLength);
if(!setRandomInBuffer(&randomBufferPtr, randomLength))
{
LOGE("## generateOneTimeKeys(): failure - random buffer init");
}
else
{
LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys);
// retrieve key pairs in keysBytesPtr
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);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## generateOneTimeKeys(): success - result=%ld", result);
}
}
}
if(NULL != randomBufferPtr)
{
free(randomBufferPtr);
}
return retCode;
}
/**
* Get "one time keys".<br>
* 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 OLM_ACCOUNT_FUNC_DEF(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 OLM_ACCOUNT_FUNC_DEF(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 OLM_ACCOUNT_FUNC_DEF(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.<br>
* The signed message is returned by the function.
* @param aMessage message to sign
* @return the signed message, null otherwise
**/
JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, jstring aMessage)
{
OlmAccount* accountPtr = NULL;
size_t signatureLength;
void* signedMsgPtr;
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 == (signedMsgPtr = (void*)malloc(signatureLength*sizeof(uint8_t))))
{
LOGE("## signMessage(): failure - signature allocation OOM");
}
else
{ // sign message
resultSign = olm_account_sign(accountPtr, (void*)messageToSign, (size_t)messageLength, signedMsgPtr, 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*)signedMsgPtr); // UTF8
LOGD("## signMessage(): success - retCode=%ld",resultSign);
}
free(signedMsgPtr);
}
// release messageToSign
env->ReleaseStringUTFChars(aMessage, messageToSign);
}
}
return signedMsgRetValue;
}
JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(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;
}

View File

@ -0,0 +1,36 @@
#ifndef _OMLACCOUNT_H
#define _OMLACCOUNT_H
#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
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

View File

@ -0,0 +1,48 @@
#ifndef _OMLJNI_H
#define _OMLJNI_H
#include <cstdlib>
#include <cstdio>
#include <string>
#include <sstream>
#include <map>
#include <jni.h>
#include <android/log.h>
#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
#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;
static const int ERROR_CODE_KO = -1;
// constants
static const int ACCOUNT_CREATION_RANDOM_MODULO = 256;
typedef struct _AccountContext
{
OlmAccount* mAccountPtr;
_AccountContext(): mAccountPtr(NULL){}
} AccountContext;
#endif

View File

@ -0,0 +1,731 @@
/*
* 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"
#include "olm_utility.h"
/**
* Init memory allocation for a session creation.<br>
* Make sure releaseSessionJni() is called when one is done with the session instance.
* @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 OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
{
OlmSession* sessionPtr = NULL;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
}
else
{
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");
}
}
/**
* Initialize a new session and return it to JAVA side.<br>
* 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 OLM_SESSION_FUNC_DEF(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.<br> 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 OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey)
{
jint retCode = ERROR_CODE_KO;
OlmSession* sessionPtr = NULL;
OlmAccount* accountPtr = NULL;
const char* theirIdentityKeyPtr = NULL;
const char* theirOneTimeKeyPtr = NULL;
uint8_t *randomBuffPtr = NULL;
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((0!=randomSize) && !setRandomInBuffer(&randomBuffPtr, randomSize))
{
LOGE("## initOutboundSessionJni(): failure - random buffer init");
}
else
{ // convert identity & one time keys to C strings
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
{
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,
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);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## initOutboundSessionJni(): success - result=%ld", sessionResult);
}
}
}
}
// **** free mem alloc ***
if(NULL!= randomBuffPtr)
{
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.<br>
* @param aOlmAccountId account instance
* @param aOneTimeKeyMsg PRE_KEY message
* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg)
{
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)))
{
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("## initInboundSessionJni(): failure - invalid message");
}
else
{ // convert message to C strings
if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
{
LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM");
}
else
{
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);
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.<br>
* @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 OLM_SESSION_FUNC_DEF(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("## initInboundSessionJni(): 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 = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg);
size_t theirIdentityKeyLength = (size_t)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.<br>
* 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 OLM_SESSION_FUNC_DEF(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 = (size_t)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);
}
}
// free local alloc
if(NULL!= messagePtr)
{
env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
}
return retCode;
}
/**
* Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.<br>
* 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 OLM_SESSION_FUNC_DEF(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 = (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()) {
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);
}
}
// 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.<br>
* @param aClearMsg clear text message
* @param [out] aEncryptedMsg ciphered message
* @return ERROR_CODE_OK if encrypt operation succeed, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint OLM_SESSION_FUNC_DEF(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 = 0;
jfieldID encryptedMsgFieldId;
jfieldID typeMsgFieldId;
LOGD("## encryptMessageJni(): IN ");
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","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((0!=randomLength) && !setRandomInBuffer(&randomBuffPtr, randomLength))
{
LOGE("## encryptMessageJni(): failure - random buffer init");
}
else
{
// alloc buffer for encrypted message
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))))
{
LOGE("## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM");
}
else
{
if(0==randomLength)
{
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,
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 encrypted buffer size
(static_cast<char*>(encryptedMsgPtr))[result] = static_cast<char>('\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 utfLength=%lu encryptedMsg=%s", result, messageType, encryptedUtfMsgLength, (const char*)encryptedMsgPtr);
}
}
}
}
// free alloc
if(NULL != clearMsgPtr)
{
env->ReleaseStringUTFChars(aClearMsg, clearMsgPtr);
}
if(NULL != randomBuffPtr)
{
free(randomBuffPtr);
}
if(NULL != encryptedMsgPtr)
{
free(encryptedMsgPtr);
}
return retCode;
}
/**
* Decrypt a message using the session.<br>
* @param aEncryptedMsg message to decrypt
* @return decrypted message if operation succeed, null otherwise
*/
JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(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 *plainTextMsgPtr = NULL;
char *tempEncryptedPtr = NULL;
LOGD("## decryptMessage(): IN ");
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## decryptMessage(): failure - invalid Session ptr=NULL");
}
else if(0 == aEncryptedMsg)
{
LOGE("## decryptMessage(): failure - invalid clear message");
}
else if(0 == (encryptedMsgJclass = env->GetObjectClass(aEncryptedMsg)))
{
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");
}
else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mType","J")))
{
LOGE("## decryptMessage(): failure - unable to get message type field");
}
else if(0 == (encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId)))
{
LOGE("## decryptMessage(): failure - JNI encrypted object ");
}
else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0)))
{
LOGE("## decryptMessage(): failure - encrypted message JNI allocation OOM");
}
else
{
// get message type
size_t encryptedMsgType = (size_t)env->GetLongField(aEncryptedMsg, typeMsgFieldId);
// get encrypted message length
size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(encryptedMsgJstring);
// create a dedicated temp buffer to be used in next Olm API calls
tempEncryptedPtr = static_cast<char*>(malloc(encryptedMsgLength*sizeof(uint8_t)));
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
LOGD("## decryptMessage(): MsgType=%ld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr);
// get max plaintext length
size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr,
static_cast<size_t>(encryptedMsgType),
static_cast<void*>(tempEncryptedPtr),
encryptedMsgLength);
// Note: tempEncryptedPtr is destroyed by olm_decrypt_max_plaintext_length()
if(maxPlainTextLength == olm_error())
{
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## decryptMessage(): failure - olm_decrypt_max_plaintext_length Msg=%s",errorMsgPtr);
}
else
{
LOGD("## decryptMessage(): maxPlaintextLength=%lu",maxPlainTextLength);
// allocate output decrypted message
plainTextMsgPtr = static_cast<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,
plainTextMsgPtr,
maxPlainTextLength);
if(plaintextLength == olm_error())
{
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## decryptMessage(): failure - olm_decrypt Msg=%s",errorMsgPtr);
}
else
{
// update decrypted buffer size
(static_cast<char*>(plainTextMsgPtr))[plaintextLength] = static_cast<char>('\0');
LOGD("## decryptMessage(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, static_cast<char*>(plainTextMsgPtr));
decryptedMsgRetValue = env->NewStringUTF(static_cast<const char*>(plainTextMsgPtr));
}
}
}
// free alloc
if(NULL != encryptedMsgPtr)
{
env->ReleaseStringUTFChars(encryptedMsgJstring, encryptedMsgPtr);
}
if(NULL != tempEncryptedPtr)
{
free(tempEncryptedPtr);
}
if(NULL != plainTextMsgPtr)
{
free(plainTextMsgPtr);
}
return decryptedMsgRetValue;
}
/**
* Get the session identifier for this session.
* @return the session identifier if operation succeed, null otherwise
*/
JNIEXPORT jstring OLM_SESSION_FUNC_DEF(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 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(lengthSessionId*sizeof(uint8_t))))
{
LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM");
}
else
{
size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
if (result == olm_error())
{
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr);
}
else
{
// update decrypted buffer size
(static_cast<char*>(sessionIdPtr))[result] = static_cast<char>('\0');
LOGD("## getSessionIdentifierJni(): success - result=%lu sessionId=%s",result, (char*)sessionIdPtr);
returnValueStr = env->NewStringUTF((const char*)sessionIdPtr);
}
free(sessionIdPtr);
}
return returnValueStr;
}

View File

@ -0,0 +1,37 @@
#ifndef _OMLSESSION_H
#define _OMLSESSION_H
#include "olm_jni.h"
#define OLM_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmSession,func_name)
#ifdef __cplusplus
extern "C" {
#endif
// 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);
// 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_decryptMessage(JNIEnv *env, jobject thiz, jobject aEncryptedMsg);
JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,141 @@
/**
* 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<aRandomSize;i++)
{
(*aBuffer2Ptr)[i] = (uint8_t)(rand()%ACCOUNT_CREATION_RANDOM_MODULO);
// debug purpose
//LOGD("## setRandomInBuffer(): randomBuffPtr[%ld]=%d",i, (*aBuffer2Ptr)[i]);
}
retCode = true;
}
return retCode;
}
/**
* 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).<br>
* @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->GetLongField(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;
}

View File

@ -0,0 +1,18 @@
#ifndef _OMLUTILITY_H
#define _OMLUTILITY_H
#ifdef __cplusplus
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
}
#endif
#endif

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">OlmSdk</string>
</resources>

View File

@ -0,0 +1 @@
include ':olm-sdk'