diff options
Diffstat (limited to 'java/android/OlmLibSdk/olm-sdk/src/androidTest')
5 files changed, 1718 insertions, 0 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 new file mode 100644 index 0000000..2c2711d --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java @@ -0,0 +1,420 @@ +/* + * 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.content.Context; +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.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +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; + private final String FILE_NAME = "SerialTestFile"; + + @BeforeClass + public static void setUpClass(){ + // enable UTF-8 specific conversion for pre Marshmallow(23) android versions, + // due to issue described here: https://github.com/eclipsesource/J2V8/issues/142 + boolean isSpecificUtf8ConversionEnabled = android.os.Build.VERSION.SDK_INT < 23; + + // load native lib + mOlmManager = new OlmManager(isSpecificUtf8ConversionEnabled); + + String olmLibVersion = mOlmManager.getOlmLibVersion(); + assertNotNull(olmLibVersion); + String olmSdkVersion = mOlmManager.getSdkOlmVersion(); + assertNotNull(olmLibVersion); + Log.d(LOG_TAG, "## setUpClass(): Versions - Android Olm SDK = "+olmSdkVersion+" Olm lib ="+olmLibVersion); + } + + @AfterClass + public static void tearDownClass() { + // TBD + } + + @Before + public void setUp() { + if(mIsAccountCreated) { + assertNotNull(mOlmAccount); + } + } + + @After + public void tearDown() { + // TBD + } + + /** + * Basic test: creation and release. + */ + @Test + public void test01CreateReleaseAccount() { + try { + mOlmAccount = new OlmAccount(); + } catch (OlmException e) { + e.printStackTrace(); + } + assertNotNull(mOlmAccount); + + mOlmAccount.releaseAccount(); + assertTrue(0 == mOlmAccount.getOlmAccountId()); + } + + @Test + public void test02CreateAccount() { + try { + mOlmAccount = new OlmAccount(); + } catch (OlmException e) { + e.printStackTrace(); + } + assertNotNull(mOlmAccount); + mIsAccountCreated = true; + } + + @Test + public void test04GetOlmAccountId() { + long olmNativeInstance = mOlmAccount.getOlmAccountId(); + Log.d(LOG_TAG,"## testGetOlmAccountId olmNativeInstance="+olmNativeInstance); + assertTrue(0!=olmNativeInstance); + } + + /** + * Test if {@link OlmAccount#identityKeys()} returns a JSON object + * that contains the following keys: {@link OlmAccount#JSON_KEY_FINGER_PRINT_KEY} + * and {@link OlmAccount#JSON_KEY_IDENTITY_KEY} + */ + @Test + public void test05IdentityKeys() { + JSONObject identityKeysJson = mOlmAccount.identityKeys(); + assertNotNull(identityKeysJson); + Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson); + + // is JSON_KEY_FINGER_PRINT_KEY present? + String fingerPrintKey = TestHelper.getFingerprintKey(identityKeysJson); + assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey)); + + // is JSON_KEY_IDENTITY_KEY present? + String identityKey = TestHelper.getIdentityKey(identityKeysJson); + assertTrue("identity key missing",!TextUtils.isEmpty(identityKey)); + } + + //**************************************************** + //***************** ONE TIME KEYS TESTS ************** + //**************************************************** + @Test + public void test06MaxOneTimeKeys() { + long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys(); + Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys); + + assertTrue(maxOneTimeKeys>0); + } + + /** + * Test one time keys generation. + */ + @Test + public void test07GenerateOneTimeKeys() { + int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER); + assertTrue(0==retValue); + } + + /** + * Test the generated amount of one time keys = GENERATION_ONE_TIME_KEYS_NUMBER. + */ + @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: + oneTimeKeysCount = generatedKeysJsonObj.length(); + + 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 = null; + try { + olmSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + long sessionId = olmSession.getOlmSessionId(); + assertTrue(0 != sessionId); + + int sessionRetCode = mOlmAccount.removeOneTimeKeysForSession(olmSession); + // test against no matching keys + assertTrue(1 == 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); + // additional tests are performed in test01VerifyEd25519Signing() + } + + + // ******************************************************** + // ************* SERIALIZATION TEST *********************** + // ******************************************************** + + @Test + public void test13Serialization() { + FileOutputStream fileOutput; + ObjectOutputStream objectOutput; + OlmAccount accountRef = null; + OlmAccount accountDeserial; + + try { + accountRef = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + + int retValue = accountRef.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER); + assertTrue(0==retValue); + + // get keys references + JSONObject identityKeysRef = accountRef.identityKeys(); + JSONObject oneTimeKeysRef = accountRef.oneTimeKeys(); + assertNotNull(identityKeysRef); + assertNotNull(oneTimeKeysRef); + + try { + Context context = getInstrumentation().getContext(); + //context.getFilesDir(); + fileOutput = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE); + + // serialize account + objectOutput = new ObjectOutputStream(fileOutput); + objectOutput.writeObject(accountRef); + objectOutput.flush(); + objectOutput.close(); + + // deserialize account + FileInputStream fileInput = context.openFileInput(FILE_NAME); + ObjectInputStream objectInput = new ObjectInputStream(fileInput); + accountDeserial = (OlmAccount) objectInput.readObject(); + objectInput.close(); + assertNotNull(accountDeserial); + + // get de-serialized keys + JSONObject identityKeysDeserial = accountDeserial.identityKeys(); + JSONObject oneTimeKeysDeserial = accountDeserial.oneTimeKeys(); + assertNotNull(identityKeysDeserial); + assertNotNull(oneTimeKeysDeserial); + + // compare identity keys + assertTrue(identityKeysDeserial.toString().equals(identityKeysRef.toString())); + + // compare onetime keys + assertTrue(oneTimeKeysDeserial.toString().equals(oneTimeKeysRef.toString())); + + accountRef.releaseAccount(); + accountDeserial.releaseAccount(); + } + catch (FileNotFoundException e) { + Log.e(LOG_TAG, "## test13Serialization(): Exception FileNotFoundException Msg=="+e.getMessage()); + } + catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "## test13Serialization(): Exception ClassNotFoundException Msg==" + e.getMessage()); + } + catch (IOException e) { + Log.e(LOG_TAG, "## test13Serialization(): Exception IOException Msg==" + e.getMessage()); + } + /*catch (OlmException e) { + Log.e(LOG_TAG, "## test13Serialization(): Exception OlmException Msg==" + e.getMessage()); + }*/ + catch (Exception e) { + Log.e(LOG_TAG, "## test13Serialization(): Exception Msg==" + e.getMessage()); + } + } + + + // **************************************************** + // *************** SANITY CHECK TESTS ***************** + // **************************************************** + + @Test + public void test14GenerateOneTimeKeysError() { + // keys number = 0 => no error + int retValue = mOlmAccount.generateOneTimeKeys(0); + assertTrue(0==retValue); + + // keys number = negative value + retValue = mOlmAccount.generateOneTimeKeys(-50); + assertTrue(-1==retValue); + } + + @Test + public void test15RemoveOneTimeKeysForSessionError() { + OlmAccount olmAccount = null; + try { + olmAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + + int sessionRetCode = olmAccount.removeOneTimeKeysForSession(null); + // test against no matching keys + assertTrue(-1 == sessionRetCode); + + olmAccount.releaseAccount(); + } + + @Test + public void test16SignMessageError() { + OlmAccount olmAccount = null; + try { + olmAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + String signedMsg = olmAccount.signMessage(null); + assertNull(signedMsg); + + olmAccount.releaseAccount(); + } + + /** + * Create multiple accounts and check that identity keys are still different. + * This test validates random series are provide enough random values. + */ + @Test + public void test17MultipleAccountCreation() { + try { + OlmAccount account1 = new OlmAccount(); + OlmAccount account2 = new OlmAccount(); + OlmAccount account3 = new OlmAccount(); + OlmAccount account4 = new OlmAccount(); + OlmAccount account5 = new OlmAccount(); + OlmAccount account6 = new OlmAccount(); + OlmAccount account7 = new OlmAccount(); + OlmAccount account8 = new OlmAccount(); + OlmAccount account9 = new OlmAccount(); + OlmAccount account10 = new OlmAccount(); + + JSONObject identityKeysJson1 = account1.identityKeys(); + JSONObject identityKeysJson2 = account2.identityKeys(); + JSONObject identityKeysJson3 = account3.identityKeys(); + JSONObject identityKeysJson4 = account4.identityKeys(); + JSONObject identityKeysJson5 = account5.identityKeys(); + JSONObject identityKeysJson6 = account6.identityKeys(); + JSONObject identityKeysJson7 = account7.identityKeys(); + JSONObject identityKeysJson8 = account8.identityKeys(); + JSONObject identityKeysJson9 = account9.identityKeys(); + JSONObject identityKeysJson10 = account10.identityKeys(); + + String identityKey1 = TestHelper.getIdentityKey(identityKeysJson1); + String identityKey2 = TestHelper.getIdentityKey(identityKeysJson2); + assertFalse(identityKey1.equals(identityKey2)); + + String identityKey3 = TestHelper.getIdentityKey(identityKeysJson3); + assertFalse(identityKey2.equals(identityKey3)); + + String identityKey4 = TestHelper.getIdentityKey(identityKeysJson4); + assertFalse(identityKey3.equals(identityKey4)); + + String identityKey5 = TestHelper.getIdentityKey(identityKeysJson5); + assertFalse(identityKey4.equals(identityKey5)); + + String identityKey6 = TestHelper.getIdentityKey(identityKeysJson6); + assertFalse(identityKey5.equals(identityKey6)); + + String identityKey7 = TestHelper.getIdentityKey(identityKeysJson7); + assertFalse(identityKey6.equals(identityKey7)); + + String identityKey8 = TestHelper.getIdentityKey(identityKeysJson8); + assertFalse(identityKey7.equals(identityKey8)); + + String identityKey9 = TestHelper.getIdentityKey(identityKeysJson9); + assertFalse(identityKey8.equals(identityKey9)); + + String identityKey10 = TestHelper.getIdentityKey(identityKeysJson10); + assertFalse(identityKey9.equals(identityKey10)); + + account1.releaseAccount(); + account2.releaseAccount(); + account3.releaseAccount(); + account4.releaseAccount(); + account5.releaseAccount(); + account6.releaseAccount(); + account7.releaseAccount(); + account8.releaseAccount(); + account9.releaseAccount(); + account10.releaseAccount(); + + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + } +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java new file mode 100644 index 0000000..6e12463 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupSessionTest.java @@ -0,0 +1,443 @@ +/* + * 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.content.Context; +import android.support.test.runner.AndroidJUnit4; +import android.text.TextUtils; +import android.util.Log; + +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; + + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class OlmGroupSessionTest { + private static final String LOG_TAG = "OlmSessionTest"; + private final String FILE_NAME_SERIAL_OUT_SESSION = "SerialOutGroupSession"; + private final String FILE_NAME_SERIAL_IN_SESSION = "SerialInGroupSession"; + + private static OlmManager mOlmManager; + private static OlmOutboundGroupSession mAliceOutboundGroupSession; + private static String mAliceSessionIdentifier; + private static long mAliceMessageIndex; + private static final String CLEAR_MESSAGE1 = "Hello!"; + private static String mAliceToBobMessage; + private static OlmInboundGroupSession mBobInboundGroupSession; + private static String mAliceOutboundSessionKey; + private static String mBobSessionIdentifier; + private static String mBobDecryptedMessage; + + @BeforeClass + public static void setUpClass(){ + + // enable UTF-8 specific conversion for pre Marshmallow(23) android versions, + // due to issue described here: https://github.com/eclipsesource/J2V8/issues/142 + boolean isSpecificUtf8ConversionEnabled = android.os.Build.VERSION.SDK_INT < 23; + + // load native lib + mOlmManager = new OlmManager(isSpecificUtf8ConversionEnabled); + + String version = mOlmManager.getOlmLibVersion(); + assertNotNull(version); + Log.d(LOG_TAG, "## setUpClass(): lib version="+version); + } + + /** + * Basic test: + * - alice creates an outbound group session + * - bob creates an inbound group session with alice's outbound session key + * - alice encrypts a message with its session + * - bob decrypts the encrypted message with its session + * - decrypted message is identical to original alice message + */ + @Test + public void test01CreateOutboundSession() { + // alice creates OUTBOUND GROUP SESSION + try { + mAliceOutboundGroupSession = new OlmOutboundGroupSession(); + } catch (OlmException e) { + assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); + } + } + + @Test + public void test02GetOutboundGroupSessionIdentifier() { + // test session ID + mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier(); + assertNotNull(mAliceSessionIdentifier); + assertTrue(mAliceSessionIdentifier.length() > 0); + } + + @Test + public void test03GetOutboundGroupSessionKey() { + // test session Key + mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey(); + assertNotNull(mAliceOutboundSessionKey); + assertTrue(mAliceOutboundSessionKey.length() > 0); + } + + @Test + public void test04GetOutboundGroupMessageIndex() { + // test message index before any encryption + mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex(); + assertTrue(0 == mAliceMessageIndex); + } + + @Test + public void test05OutboundGroupEncryptMessage() { + // alice encrypts a message to bob + mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1); + assertFalse(TextUtils.isEmpty(mAliceToBobMessage)); + + // test message index after encryption is incremented + mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex(); + assertTrue(1 == mAliceMessageIndex); + } + + @Test + public void test06CreateInboundGroupSession() { + // bob creates INBOUND GROUP SESSION with alice outbound key + try { + mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey); + } catch (OlmException e) { + assertTrue("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false); + } + } + + @Test + public void test08GetInboundGroupSessionIdentifier() { + // check both session identifiers are equals + mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier(); + assertFalse(TextUtils.isEmpty(mBobSessionIdentifier)); + } + + @Test + public void test09SessionIdentifiersAreIdentical() { + // check both session identifiers are equals: alice vs bob + assertTrue(mAliceSessionIdentifier.equals(mBobSessionIdentifier)); + } + + @Test + public void test10InboundDecryptMessage() { + // test decrypted message + mBobDecryptedMessage = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage); + assertFalse(TextUtils.isEmpty(mBobDecryptedMessage)); + } + + @Test + public void test11InboundDecryptedMessageIdentical() { + // test decrypted message + assertTrue(mBobDecryptedMessage.equals(CLEAR_MESSAGE1)); + } + + @Test + public void test12ReleaseOutboundSession() { + // release group sessions + mAliceOutboundGroupSession.releaseSession(); + } + + @Test + public void test13ReleaseInboundSession() { + // release group sessions + mBobInboundGroupSession.releaseSession(); + } + + @Test + public void test14CheckUnreleaseedCount() { + assertTrue(0==mAliceOutboundGroupSession.getUnreleasedCount()); + assertTrue(0==mBobInboundGroupSession.getUnreleasedCount()); + } + + @Test + public void test15SerializeOutboundSession() { + OlmOutboundGroupSession outboundGroupSessionRef=null; + OlmOutboundGroupSession outboundGroupSessionSerial; + + // create one OUTBOUND GROUP SESSION + try { + outboundGroupSessionRef = new OlmOutboundGroupSession(); + } catch (OlmException e) { + assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); + } + assertNotNull(outboundGroupSessionRef); + + + // serialize alice session + Context context = getInstrumentation().getContext(); + try { + FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_OUT_SESSION, Context.MODE_PRIVATE); + ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput); + objectOutput.writeObject(outboundGroupSessionRef); + objectOutput.flush(); + objectOutput.close(); + + // deserialize session + FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_OUT_SESSION); + ObjectInputStream objectInput = new ObjectInputStream(fileInput); + outboundGroupSessionSerial = (OlmOutboundGroupSession) objectInput.readObject(); + assertNotNull(outboundGroupSessionSerial); + objectInput.close(); + + // get sessions keys + String sessionKeyRef = outboundGroupSessionRef.sessionKey(); + String sessionKeySerial = outboundGroupSessionSerial.sessionKey(); + assertFalse(TextUtils.isEmpty(sessionKeyRef)); + assertFalse(TextUtils.isEmpty(sessionKeySerial)); + + // session keys comparison + assertTrue(sessionKeyRef.equals(sessionKeySerial)); + + // get sessions IDs + String sessionIdRef = outboundGroupSessionRef.sessionIdentifier(); + String sessionIdSerial = outboundGroupSessionSerial.sessionIdentifier(); + assertFalse(TextUtils.isEmpty(sessionIdRef)); + assertFalse(TextUtils.isEmpty(sessionIdSerial)); + + // session IDs comparison + assertTrue(sessionIdRef.equals(sessionIdSerial)); + + outboundGroupSessionRef.releaseSession(); + outboundGroupSessionSerial.releaseSession(); + + assertTrue(0==outboundGroupSessionRef.getUnreleasedCount()); + assertTrue(0==outboundGroupSessionSerial.getUnreleasedCount()); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception FileNotFoundException Msg=="+e.getMessage()); + } catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage()); + } catch (OlmException e) { + Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception OlmException Msg==" + e.getMessage()); + } catch (IOException e) { + Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception IOException Msg==" + e.getMessage()); + } catch (Exception e) { + Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception Msg==" + e.getMessage()); + } + } + + @Test + public void test16SerializeInboundSession() { + OlmOutboundGroupSession aliceOutboundGroupSession=null; + OlmInboundGroupSession bobInboundGroupSessionRef=null; + OlmInboundGroupSession bobInboundGroupSessionSerial; + + // alice creates OUTBOUND GROUP SESSION + try { + aliceOutboundGroupSession = new OlmOutboundGroupSession(); + } catch (OlmException e) { + assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); + } + assertNotNull(aliceOutboundGroupSession); + + // get the session key from the outbound group session + String sessionKeyRef = aliceOutboundGroupSession.sessionKey(); + assertNotNull(sessionKeyRef); + + // bob creates INBOUND GROUP SESSION + try { + bobInboundGroupSessionRef = new OlmInboundGroupSession(sessionKeyRef); + } catch (OlmException e) { + assertTrue("Exception in OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false); + } + assertNotNull(bobInboundGroupSessionRef); + + + // serialize alice session + Context context = getInstrumentation().getContext(); + try { + FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_IN_SESSION, Context.MODE_PRIVATE); + ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput); + objectOutput.writeObject(bobInboundGroupSessionRef); + objectOutput.flush(); + objectOutput.close(); + + // deserialize session + FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_IN_SESSION); + ObjectInputStream objectInput = new ObjectInputStream(fileInput); + bobInboundGroupSessionSerial = (OlmInboundGroupSession)objectInput.readObject(); + assertNotNull(bobInboundGroupSessionSerial); + objectInput.close(); + + // get sessions IDs + String aliceSessionId = aliceOutboundGroupSession.sessionIdentifier(); + String sessionIdRef = bobInboundGroupSessionRef.sessionIdentifier(); + String sessionIdSerial = bobInboundGroupSessionSerial.sessionIdentifier(); + assertFalse(TextUtils.isEmpty(aliceSessionId)); + assertFalse(TextUtils.isEmpty(sessionIdRef)); + assertFalse(TextUtils.isEmpty(sessionIdSerial)); + + // session IDs comparison + assertTrue(aliceSessionId.equals(sessionIdSerial)); + assertTrue(sessionIdRef.equals(sessionIdSerial)); + + aliceOutboundGroupSession.releaseSession(); + bobInboundGroupSessionRef.releaseSession(); + bobInboundGroupSessionSerial.releaseSession(); + + assertTrue(0==aliceOutboundGroupSession.getUnreleasedCount()); + assertTrue(0==bobInboundGroupSessionRef.getUnreleasedCount()); + assertTrue(0==bobInboundGroupSessionSerial.getUnreleasedCount()); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception FileNotFoundException Msg=="+e.getMessage()); + } catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage()); + } catch (OlmException e) { + Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception OlmException Msg==" + e.getMessage()); + } catch (IOException e) { + Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception IOException Msg==" + e.getMessage()); + } catch (Exception e) { + Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception Msg==" + e.getMessage()); + } + } + + /** + * Create multiple outbound group sessions and check that session Keys are different. + * This test validates random series are provide enough random values. + */ + @Test + public void test17MultipleOutboundSession() { + OlmOutboundGroupSession outboundGroupSession1; + OlmOutboundGroupSession outboundGroupSession2; + OlmOutboundGroupSession outboundGroupSession3; + OlmOutboundGroupSession outboundGroupSession4; + OlmOutboundGroupSession outboundGroupSession5; + OlmOutboundGroupSession outboundGroupSession6; + OlmOutboundGroupSession outboundGroupSession7; + OlmOutboundGroupSession outboundGroupSession8; + + try { + outboundGroupSession1 = new OlmOutboundGroupSession(); + outboundGroupSession2 = new OlmOutboundGroupSession(); + outboundGroupSession3 = new OlmOutboundGroupSession(); + outboundGroupSession4 = new OlmOutboundGroupSession(); + outboundGroupSession5 = new OlmOutboundGroupSession(); + outboundGroupSession6 = new OlmOutboundGroupSession(); + outboundGroupSession7 = new OlmOutboundGroupSession(); + outboundGroupSession8 = new OlmOutboundGroupSession(); + + // get the session key from the outbound group sessions + String sessionKey1 = outboundGroupSession1.sessionKey(); + String sessionKey2 = outboundGroupSession2.sessionKey(); + assertFalse(sessionKey1.equals(sessionKey2)); + + String sessionKey3 = outboundGroupSession3.sessionKey(); + assertFalse(sessionKey2.equals(sessionKey3)); + + String sessionKey4 = outboundGroupSession4.sessionKey(); + assertFalse(sessionKey3.equals(sessionKey4)); + + String sessionKey5 = outboundGroupSession5.sessionKey(); + assertFalse(sessionKey4.equals(sessionKey5)); + + String sessionKey6 = outboundGroupSession6.sessionKey(); + assertFalse(sessionKey5.equals(sessionKey6)); + + String sessionKey7 = outboundGroupSession7.sessionKey(); + assertFalse(sessionKey6.equals(sessionKey7)); + + String sessionKey8 = outboundGroupSession8.sessionKey(); + assertFalse(sessionKey7.equals(sessionKey8)); + + // get the session IDs from the outbound group sessions + String sessionId1 = outboundGroupSession1.sessionIdentifier(); + String sessionId2 = outboundGroupSession2.sessionIdentifier(); + assertFalse(sessionId1.equals(sessionId2)); + + String sessionId3 = outboundGroupSession3.sessionKey(); + assertFalse(sessionId2.equals(sessionId3)); + + String sessionId4 = outboundGroupSession4.sessionKey(); + assertFalse(sessionId3.equals(sessionId4)); + + String sessionId5 = outboundGroupSession5.sessionKey(); + assertFalse(sessionId4.equals(sessionId5)); + + String sessionId6 = outboundGroupSession6.sessionKey(); + assertFalse(sessionId5.equals(sessionId6)); + + String sessionId7 = outboundGroupSession7.sessionKey(); + assertFalse(sessionId6.equals(sessionId7)); + + String sessionId8 = outboundGroupSession8.sessionKey(); + assertFalse(sessionId7.equals(sessionId8)); + + outboundGroupSession1.releaseSession(); + outboundGroupSession2.releaseSession(); + outboundGroupSession3.releaseSession(); + outboundGroupSession4.releaseSession(); + outboundGroupSession5.releaseSession(); + outboundGroupSession6.releaseSession(); + outboundGroupSession7.releaseSession(); + outboundGroupSession8.releaseSession(); + + assertTrue(0==outboundGroupSession1.getUnreleasedCount()); + assertTrue(0==outboundGroupSession2.getUnreleasedCount()); + assertTrue(0==outboundGroupSession3.getUnreleasedCount()); + assertTrue(0==outboundGroupSession4.getUnreleasedCount()); + assertTrue(0==outboundGroupSession5.getUnreleasedCount()); + assertTrue(0==outboundGroupSession6.getUnreleasedCount()); + assertTrue(0==outboundGroupSession7.getUnreleasedCount()); + assertTrue(0==outboundGroupSession8.getUnreleasedCount()); + } catch (OlmException e) { + assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); + } + } + + /** + * Specific test for the following run time error: + * "JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xf0 in call to NewStringUTF".<br> + * When the msg to decrypt contain emojis, depending on the android platform, the NewStringUTF() behaves differently and + * can even crash. + * This issue is described in details here: https://github.com/eclipsesource/J2V8/issues/142 + */ + @Test + public void test18TestBadCharacterCrashInDecrypt() { + OlmInboundGroupSession bobInboundGroupSession=null; + + // values taken from a "real life" crash case + String sessionKeyRef = "AgAAAAycZE6AekIctJWYxd2AWLOY15YmxZODm/WkgbpWkyycp6ytSp/R+wo84jRrzBNWmv6ySLTZ9R0EDOk9VI2eZyQ6Efdwyo1mAvrWvTkZl9yALPdkOIVHywyG65f1SNiLrnsln3hgsT1vUrISGyKtsljoUgQpr3JDPEhD0ilAi63QBjhnGCW252b+7nF+43rb6O6lwm93LaVwe2341Gdp6EkhTUvetALezEqDOtKN00wVqAbq0RQAnUJIowxHbMswg+FyoR1K1oCjnVEoF23O9xlAn5g1XtuBZP3moJlR2lwsBA"; + String msgToDecryptWithEmoji = "AwgNEpABpjs+tYF+0y8bWtzAgYAC3N55p5cPJEEiGPU1kxIHSY7f2aG5Fj4wmcsXUkhDv0UePj922kgf+Q4dFsPHKq2aVA93n8DJAQ/FRfcM98B9E6sKCZ/PsCF78uBvF12Aaq9D3pUHBopdd7llUfVq29d5y6ZwX5VDoqV2utsATkKjXYV9CbfZuvvBMQ30ZLjEtyUUBJDY9K4FxEFcULytA/IkVnATTG9ERuLF/yB6ukSFR+iUWRYAmtuOuU0k9BvaqezbGqNoK5Grlkes+dYX6/0yUObumcw9/iAI"; + + // bob creates INBOUND GROUP SESSION + try { + bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef); + } catch (OlmException e) { + assertTrue("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getExceptionCode(), false); + } + + String decryptedMessage = bobInboundGroupSession.decryptMessage(msgToDecryptWithEmoji); + assertNotNull(decryptedMessage); + } + +} + 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..1aeb3fb --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java @@ -0,0 +1,641 @@ +/* + * 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.content.Context; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; + +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.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; +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 final String INVALID_PRE_KEY = "invalid PRE KEY hu hu!"; + private final String FILE_NAME_SERIAL_SESSION = "SerialSession"; + private final int ONE_TIME_KEYS_NUMBER = 4; + + private static OlmManager mOlmManager; + + @BeforeClass + public static void setUpClass(){ + // enable UTF-8 specific conversion for pre Marshmallow(23) android versions, + // due to issue described here: https://github.com/eclipsesource/J2V8/issues/142 + boolean isSpecificUtf8ConversionEnabled = android.os.Build.VERSION.SDK_INT < 23; + + // load native lib + mOlmManager = new OlmManager(isSpecificUtf8ConversionEnabled); + + 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; + OlmAccount bobAccount = null; + OlmAccount aliceAccount = null; + + // ALICE & BOB ACCOUNTS CREATION + try { + aliceAccount = new OlmAccount(); + bobAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // get bob identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeysJson); + assertTrue(null!=bobIdentityKey); + + // get bob one time keys + assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeysJsonObj,1); + assertNotNull(bobOneTimeKey); + + // CREATE ALICE SESSION + OlmSession aliceSession = null; + try { + aliceSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + 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); + assertNotNull(encryptedMsgToBob.mCipherText); + Log.d(LOG_TAG,"## test01AliceToBob(): encryptedMsg="+encryptedMsgToBob.mCipherText); + + // CREATE BOB INBOUND SESSION and decrypt message from alice + OlmSession bobSession = null; + try { + bobSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + assertTrue(0!=bobSession.getOlmSessionId()); + assertTrue(0==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)); + + // release accounts + bobAccount.releaseAccount(); + aliceAccount.releaseAccount(); + assertTrue(0==bobAccount.getUnreleasedCount()); + assertTrue(0==aliceAccount.getUnreleasedCount()); + + // release sessions + bobSession.releaseSession(); + aliceSession.releaseSession(); + assertTrue(0==bobSession.getUnreleasedCount()); + assertTrue(0==aliceSession.getUnreleasedCount()); + } + + + /** + * 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 + * - alice encrypts a message + * - bob decrypts the encrypted message + */ + @Test + public void test02AliceToBobBackAndForth() { + String bobIdentityKey; + String bobOneTimeKey; + OlmAccount aliceAccount = null; + OlmAccount bobAccount = null; + + // creates alice & bob accounts + try { + aliceAccount = new OlmAccount(); + bobAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // get bob identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeysJson); + assertTrue(null!=bobIdentityKey); + + // get bob one time keys + assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeysJsonObj,1); + assertNotNull(bobOneTimeKey); + + // CREATE ALICE SESSION + OlmSession aliceSession = null; + try { + aliceSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + 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); + assertNotNull(encryptedAliceToBobMsg1.mCipherText); + + // CREATE BOB INBOUND SESSION and decrypt message from alice + OlmSession bobSession = null; + try { + bobSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + assertTrue(0!=bobSession.getOlmSessionId()); + assertTrue(0==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)); + + // BACK/FORTH MESSAGE COMPARISON + String clearMsg1 = "Hello I'm Bob!"; + String clearMsg2 = "Isn't life grand?"; + String clearMsg3 = "Let's go to the opera."; + + // bob encrypts messages + OlmMessage encryptedMsg1 = bobSession.encryptMessage(clearMsg1); + assertNotNull(encryptedMsg1); + OlmMessage encryptedMsg2 = bobSession.encryptMessage(clearMsg2); + assertNotNull(encryptedMsg2); + OlmMessage encryptedMsg3 = bobSession.encryptMessage(clearMsg3); + assertNotNull(encryptedMsg3); + + // alice decrypts bob's messages + String decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1); + assertNotNull(decryptedMsg1); + String decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2); + assertNotNull(decryptedMsg2); + String decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3); + assertNotNull(decryptedMsg3); + + // comparison tests + assertTrue(clearMsg1.equals(decryptedMsg1)); + assertTrue(clearMsg2.equals(decryptedMsg2)); + assertTrue(clearMsg3.equals(decryptedMsg3)); + + // and one more from alice to bob + clearMsg1 = "another message from Alice to Bob!!"; + encryptedMsg1 = aliceSession.encryptMessage(clearMsg1); + assertNotNull(encryptedMsg1); + decryptedMsg1 = bobSession.decryptMessage(encryptedMsg1); + assertNotNull(decryptedMsg1); + assertTrue(clearMsg1.equals(decryptedMsg1)); + + // comparison test + assertTrue(clearMsg1.equals(decryptedMsg1)); + + // clean objects.. + assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession)); + bobAccount.releaseAccount(); + aliceAccount.releaseAccount(); + assertTrue(0==bobAccount.getUnreleasedCount()); + assertTrue(0==aliceAccount.getUnreleasedCount()); + + bobSession.releaseSession(); + aliceSession.releaseSession(); + assertTrue(0==bobSession.getUnreleasedCount()); + assertTrue(0==aliceSession.getUnreleasedCount()); + } + + + @Test + public void test03AliceBobSessionId() { + // creates alice & bob accounts + OlmAccount aliceAccount = null; + OlmAccount bobAccount = null; + try { + aliceAccount = new OlmAccount(); + bobAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // CREATE ALICE SESSION + + OlmSession aliceSession = null; + try { + aliceSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + assertTrue(0!=aliceSession.getOlmSessionId()); + + // CREATE ALICE SESSION + OlmSession bobSession = null; + try { + bobSession = new OlmSession(); + } catch (OlmException e) { + e.printStackTrace(); + } + 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)); + + aliceAccount.releaseAccount(); + bobAccount.releaseAccount(); + assertTrue(0==aliceAccount.getUnreleasedCount()); + assertTrue(0==bobAccount.getUnreleasedCount()); + + bobSession.releaseSession(); + aliceSession.releaseSession(); + assertTrue(0==bobSession.getUnreleasedCount()); + assertTrue(0==aliceSession.getUnreleasedCount()); + } + + @Test + public void test04MatchInboundSession() { + OlmAccount aliceAccount=null, bobAccount=null; + OlmSession aliceSession = null, bobSession = null; + + // ACCOUNTS CREATION + try { + aliceAccount = new OlmAccount(); + bobAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(), false); + } + + // CREATE ALICE SESSION + try { + aliceSession = new OlmSession(); + bobSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg=" + e.getMessage(), false); + } + + // get bob/luke identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + JSONObject aliceIdentityKeysJson = aliceAccount.identityKeys(); + String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeysJson); + String aliceIdentityKey = TestHelper.getIdentityKey(aliceIdentityKeysJson); + + // get bob/luke one time keys + assertTrue(0 == bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + assertTrue(0 == aliceAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + String bobOneTimeKey1 = TestHelper.getOneTimeKey(bobOneTimeKeysJsonObj, 1); + + // create alice inbound session for bob + assertTrue(0==aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey1)); + + String aliceClearMsg = "hello helooo to bob!"; + OlmMessage encryptedAliceToBobMsg1 = aliceSession.encryptMessage(aliceClearMsg); + assertFalse(bobSession.matchesInboundSession(encryptedAliceToBobMsg1.mCipherText)); + + // init bob session with alice PRE KEY + assertTrue(0==bobSession.initInboundSessionWithAccount(bobAccount, encryptedAliceToBobMsg1.mCipherText)); + + // test matchesInboundSession() and matchesInboundSessionFrom() + assertTrue(bobSession.matchesInboundSession(encryptedAliceToBobMsg1.mCipherText)); + assertTrue(bobSession.matchesInboundSessionFrom(aliceIdentityKey, encryptedAliceToBobMsg1.mCipherText)); + // following requires olm native lib new version with https://github.com/matrix-org/olm-backup/commit/7e9f3bebb8390f975a76c0188ce4cb460fe6692e + //assertTrue(false==bobSession.matchesInboundSessionFrom(bobIdentityKey, encryptedAliceToBobMsg1.mCipherText)); + + // release objects + assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession)); + aliceAccount.releaseAccount(); + bobAccount.releaseAccount(); + assertTrue(0==aliceAccount.getUnreleasedCount()); + assertTrue(0==bobAccount.getUnreleasedCount()); + + aliceSession.releaseSession(); + bobSession.releaseSession(); + assertTrue(0==aliceSession.getUnreleasedCount()); + assertTrue(0==bobSession.getUnreleasedCount()); + } + + // ******************************************************** + // ************* SERIALIZATION TEST *********************** + // ******************************************************** + /** + * Same as {@link #test02AliceToBobBackAndForth()}, but alice's session + * is serialized and de-serialized before performing the final + * comparison (encrypt vs ) + */ + @Test + public void test05SessionSerialization() { + final int ONE_TIME_KEYS_NUMBER = 1; + String bobIdentityKey; + String bobOneTimeKey; + OlmAccount aliceAccount = null; + OlmAccount bobAccount = null; + OlmSession aliceSessionDeserial = null; + + // creates alice & bob accounts + try { + aliceAccount = new OlmAccount(); + bobAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + + // test accounts creation + assertTrue(0!=bobAccount.getOlmAccountId()); + assertTrue(0!=aliceAccount.getOlmAccountId()); + + // get bob identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeysJson); + assertTrue(null!=bobIdentityKey); + + // get bob one time keys + assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeysJsonObj,1); + assertNotNull(bobOneTimeKey); + + // CREATE ALICE SESSION + OlmSession aliceSession = null; + try { + aliceSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + 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); + assertNotNull(encryptedAliceToBobMsg1.mCipherText); + + // CREATE BOB INBOUND SESSION and decrypt message from alice + OlmSession bobSession = null; + try { + bobSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + assertTrue(0!=bobSession.getOlmSessionId()); + assertTrue(0==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)); + + // BACK/FORTH MESSAGE COMPARISON + String clearMsg1 = "Hello I'm Bob!"; + String clearMsg2 = "Isn't life grand?"; + String clearMsg3 = "Let's go to the opera."; + + // bob encrypts messages + OlmMessage encryptedMsg1 = bobSession.encryptMessage(clearMsg1); + assertNotNull(encryptedMsg1); + OlmMessage encryptedMsg2 = bobSession.encryptMessage(clearMsg2); + assertNotNull(encryptedMsg2); + OlmMessage encryptedMsg3 = bobSession.encryptMessage(clearMsg3); + assertNotNull(encryptedMsg3); + + // serialize alice session + Context context = getInstrumentation().getContext(); + try { + FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_SESSION, Context.MODE_PRIVATE); + ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput); + objectOutput.writeObject(aliceSession); + objectOutput.flush(); + objectOutput.close(); + + // deserialize session + FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_SESSION); + ObjectInputStream objectInput = new ObjectInputStream(fileInput); + aliceSessionDeserial = (OlmSession) objectInput.readObject(); + objectInput.close(); + + // test deserialize return value + assertNotNull(aliceSessionDeserial); + + // de-serialized alice session decrypts bob's messages + String decryptedMsg1 = aliceSessionDeserial.decryptMessage(encryptedMsg1); + assertNotNull(decryptedMsg1); + String decryptedMsg2 = aliceSessionDeserial.decryptMessage(encryptedMsg2); + assertNotNull(decryptedMsg2); + String decryptedMsg3 = aliceSessionDeserial.decryptMessage(encryptedMsg3); + assertNotNull(decryptedMsg3); + + // comparison tests + assertTrue(clearMsg1.equals(decryptedMsg1)); + assertTrue(clearMsg2.equals(decryptedMsg2)); + assertTrue(clearMsg3.equals(decryptedMsg3)); + + // clean objects.. + assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession)); + bobAccount.releaseAccount(); + aliceAccount.releaseAccount(); + assertTrue(0==bobAccount.getUnreleasedCount()); + assertTrue(0==aliceAccount.getUnreleasedCount()); + + bobSession.releaseSession(); + aliceSession.releaseSession(); + aliceSessionDeserial.releaseSession(); + assertTrue(0==bobSession.getUnreleasedCount()); + assertTrue(0==aliceSession.getUnreleasedCount()); + assertTrue(0==aliceSessionDeserial.getUnreleasedCount()); + } + catch (FileNotFoundException e) { + Log.e(LOG_TAG, "## test03SessionSerialization(): Exception FileNotFoundException Msg=="+e.getMessage()); + } + catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "## test03SessionSerialization(): Exception ClassNotFoundException Msg==" + e.getMessage()); + } + catch (IOException e) { + Log.e(LOG_TAG, "## test03SessionSerialization(): Exception IOException Msg==" + e.getMessage()); + } + /*catch (OlmException e) { + Log.e(LOG_TAG, "## test03SessionSerialization(): Exception OlmException Msg==" + e.getMessage()); + }*/ + catch (Exception e) { + Log.e(LOG_TAG, "## test03SessionSerialization(): Exception Msg==" + e.getMessage()); + } + } + + + // **************************************************** + // *************** SANITY CHECK TESTS ***************** + // **************************************************** + + @Test + public void test06SanityCheckErrors() { + final int ONE_TIME_KEYS_NUMBER = 5; + OlmAccount bobAccount = null; + OlmAccount aliceAccount = null; + + // ALICE & BOB ACCOUNTS CREATION + try { + aliceAccount = new OlmAccount(); + bobAccount = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(), false); + } + + // get bob identity key + JSONObject bobIdentityKeysJson = bobAccount.identityKeys(); + String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeysJson); + assertTrue(null != bobIdentityKey); + + // get bob one time keys + assertTrue(0 == bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER)); + JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys(); + assertNotNull(bobOneTimeKeysJsonObj); + String bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeysJsonObj,1); + assertNotNull(bobOneTimeKey); + + // CREATE ALICE SESSION + OlmSession aliceSession = null; + try { + aliceSession = new OlmSession(); + } catch (OlmException e) { + assertTrue("Exception Msg=" + e.getMessage(), false); + } + + // SANITY CHECK TESTS FOR: initOutboundSessionWithAccount() + assertTrue(-1==aliceSession.initOutboundSessionWithAccount(null, bobIdentityKey, bobOneTimeKey)); + assertTrue(-1==aliceSession.initOutboundSessionWithAccount(aliceAccount, null, bobOneTimeKey)); + assertTrue(-1==aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, null)); + assertTrue(-1==aliceSession.initOutboundSessionWithAccount(null, null, null)); + + // init properly + assertTrue(0==aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey)); + + // SANITY CHECK TESTS FOR: encryptMessage() + assertTrue(null==aliceSession.encryptMessage(null)); + + // encrypt properly + OlmMessage encryptedMsgToBob = aliceSession.encryptMessage("A message for bob"); + assertNotNull(encryptedMsgToBob); + + // SANITY CHECK TESTS FOR: initInboundSessionWithAccount() + OlmSession bobSession = null; + try { + bobSession = new OlmSession(); + assertTrue(-1==bobSession.initInboundSessionWithAccount(null, encryptedMsgToBob.mCipherText)); + assertTrue(-1==bobSession.initInboundSessionWithAccount(bobAccount, null)); + assertTrue(-1==bobSession.initInboundSessionWithAccount(bobAccount, INVALID_PRE_KEY)); + // init properly + assertTrue(0==bobSession.initInboundSessionWithAccount(bobAccount, encryptedMsgToBob.mCipherText)); + } catch (OlmException e) { + assertTrue("Exception Msg="+e.getMessage(), false); + } + + // SANITY CHECK TESTS FOR: decryptMessage() + String decryptedMsg = aliceSession.decryptMessage(null); + assertTrue(null==decryptedMsg); + + // SANITY CHECK TESTS FOR: matchesInboundSession() + assertTrue(!aliceSession.matchesInboundSession(null)); + + // SANITY CHECK TESTS FOR: matchesInboundSessionFrom() + assertTrue(!aliceSession.matchesInboundSessionFrom(null,null)); + + // release objects + assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession)); + aliceAccount.releaseAccount(); + bobAccount.releaseAccount(); + assertTrue(0==aliceAccount.getUnreleasedCount()); + assertTrue(0==bobAccount.getUnreleasedCount()); + + aliceSession.releaseSession(); + bobSession.releaseSession(); + assertTrue(0==aliceSession.getUnreleasedCount()); + assertTrue(0==bobSession.getUnreleasedCount()); + } + +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmUtilityTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmUtilityTest.java new file mode 100644 index 0000000..3006344 --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmUtilityTest.java @@ -0,0 +1,124 @@ +/* + * 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.support.test.runner.AndroidJUnit4; +import android.text.TextUtils; +import android.util.Log; + +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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class OlmUtilityTest { + private static final String LOG_TAG = "OlmAccountTest"; + private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50; + + private static OlmManager mOlmManager; + + @BeforeClass + public static void setUpClass(){ + // enable UTF-8 specific conversion for pre Marshmallow(23) android versions, + // due to issue described here: https://github.com/eclipsesource/J2V8/issues/142 + boolean isSpecificUtf8ConversionEnabled = android.os.Build.VERSION.SDK_INT < 23; + + // load native lib + mOlmManager = new OlmManager(isSpecificUtf8ConversionEnabled); + + String version = mOlmManager.getOlmLibVersion(); + assertNotNull(version); + Log.d(LOG_TAG, "## setUpClass(): lib version="+version); + } + + /** + * Test the signing API + */ + @Test + public void test01VerifyEd25519Signing() { + String fingerPrintKey = null; + StringBuffer errorMsg = new StringBuffer(); + String message = "{\"algorithms\":[\"m.megolm.v1.aes-sha2\",\"m.olm.v1.curve25519-aes-sha2\"],\"device_id\":\"YMBYCWTWCG\",\"keys\":{\"curve25519:YMBYCWTWCG\":\"KZFa5YUXV2EOdhK8dcGMMHWB67stdgAP4+xwiS69mCU\",\"ed25519:YMBYCWTWCG\":\"0cEgQJJqjgtXUGp4ZXQQmh36RAxwxr8HJw2E9v1gvA0\"},\"user_id\":\"@mxBob14774891254276b253f42-f267-43ec-bad9-767142bfea30:localhost:8480\"}"; + OlmAccount account = null; + + // create account + try { + account = new OlmAccount(); + } catch (OlmException e) { + assertTrue(e.getMessage(),false); + } + assertNotNull(account); + + // sign message + String messageSignature = account.signMessage(message); + assertNotNull(messageSignature); + + // get identities key (finger print key) + JSONObject identityKeysJson = account.identityKeys(); + assertNotNull(identityKeysJson); + fingerPrintKey = TestHelper.getFingerprintKey(identityKeysJson); + assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey)); + + // instantiate utility object + OlmUtility utility = new OlmUtility(); + + // verify signature + errorMsg.append("init with anything"); + boolean isVerified = utility.verifyEd25519Signature(messageSignature, fingerPrintKey, message, errorMsg); + assertTrue(isVerified); + assertTrue(String.valueOf(errorMsg).isEmpty()); + + // check a bad signature is detected => errorMsg = BAD_MESSAGE_MAC + String badSignature = "Bad signature Bad signature Bad signature.."; + isVerified = utility.verifyEd25519Signature(badSignature, fingerPrintKey, message, errorMsg); + assertFalse(isVerified); + assertFalse(String.valueOf(errorMsg).isEmpty()); + + // check bad fingerprint size => errorMsg = INVALID_BASE64 + String badSizeFingerPrintKey = fingerPrintKey.substring(fingerPrintKey.length()/2); + isVerified = utility.verifyEd25519Signature(messageSignature, badSizeFingerPrintKey, message, errorMsg); + assertFalse(isVerified); + assertFalse(String.valueOf(errorMsg).isEmpty()); + + utility.releaseUtility(); + assertTrue(0==utility.getUnreleasedCount()); + + account.releaseAccount(); + assertTrue(0==account.getUnreleasedCount()); + } + + + @Test + public void test02sha256() { + OlmUtility utility = new OlmUtility(); + String msgToHash = "The quick brown fox jumps over the lazy dog"; + + String hashResult = utility.sha256(msgToHash); + assertFalse(TextUtils.isEmpty(hashResult)); + + utility.releaseUtility(); + assertTrue(0==utility.getUnreleasedCount()); + } +} diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/TestHelper.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/TestHelper.java new file mode 100644 index 0000000..363ab7a --- /dev/null +++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/TestHelper.java @@ -0,0 +1,90 @@ +/* + * 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 org.json.JSONException; +import org.json.JSONObject; + +import java.util.Iterator; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Helper class providing helper methods used in the Olm Android SDK unit tests. + */ +public class TestHelper { + + /** + * Return the identity key {@link OlmAccount#JSON_KEY_IDENTITY_KEY} from the JSON object. + * @param aIdentityKeysObj JSON result of {@link OlmAccount#identityKeys()} + * @return identity key string if operation succeed, null otherwise + */ + static public String getIdentityKey(JSONObject aIdentityKeysObj){ + String idKey = null; + + try { + idKey = aIdentityKeysObj.getString(OlmAccount.JSON_KEY_IDENTITY_KEY); + } catch (JSONException e) { + assertTrue("Exception MSg=" + e.getMessage(), false); + } + return idKey; + } + + /** + * Return the fingerprint key {@link OlmAccount#JSON_KEY_FINGER_PRINT_KEY} from the JSON object. + * @param aIdentityKeysObj JSON result of {@link OlmAccount#identityKeys()} + * @return fingerprint key string if operation succeed, null otherwise + */ + static public String getFingerprintKey(JSONObject aIdentityKeysObj){ + String fingerprintKey = null; + + try { + fingerprintKey = aIdentityKeysObj.getString(OlmAccount.JSON_KEY_FINGER_PRINT_KEY); + } catch (JSONException e) { + assertTrue("Exception MSg=" + e.getMessage(), false); + } + return fingerprintKey; + } + + /** + * Return the first one time key from the JSON object. + * @param aIdentityKeysObj JSON result of {@link OlmAccount#oneTimeKeys()} + * @param aKeyPosition the position of the key to be retrieved + * @return one time key string if operation succeed, null otherwise + */ + static public String getOneTimeKey(JSONObject aIdentityKeysObj, int aKeyPosition) { + String firstOneTimeKey = null; + int i=0; + + try { + JSONObject generatedKeys = aIdentityKeysObj.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY); + assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY + " object is missing", generatedKeys); + + Iterator<String> generatedKeysIt = generatedKeys.keys(); + while(i<aKeyPosition) { + if (generatedKeysIt.hasNext()) { + firstOneTimeKey = generatedKeys.getString(generatedKeysIt.next()); + i++; + } + } + } catch (JSONException e) { + assertTrue("Exception Msg=" + e.getMessage(), false); + } + return firstOneTimeKey; + } +} |