Add OlmUtility class

- add unit tests for OlmUtility
- rename OlmGroupTest to OlmGroupSessionTest
- update OlmException
release-v2.2.0
pedroGitt 2016-10-18 16:05:28 +02:00
parent 034fa6be40
commit 139402611a
10 changed files with 273 additions and 89 deletions

View File

@ -173,41 +173,6 @@ public class OlmAccountTest {
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
// additional tests are performed in test01VerifyEd25519Signing()
}
private void testJni(){
OlmManager mgr = new OlmManager();
String versionLib = mgr.getOlmLibVersion();
Log.d(LOG_TAG, "## testJni(): lib version="+versionLib);
OlmAccount account = new OlmAccount();
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

@ -17,16 +17,16 @@ import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OlmGroupTest {
public class OlmGroupSessionTest {
private static final String LOG_TAG = "OlmSessionTest";
private static OlmManager mOlmManager;
private static OlmOutboundGroupSession mAliceOutboundSession;
private static OlmOutboundGroupSession mAliceOutboundGroupSession;
private static String mAliceSessionIdentifier;
private static long mAliceMessageIndex;
public static final String CLEAR_MESSAGE1 = "Hello!";
private static String mAliceToBobMessage;
private static OlmInboundGroupSession mBobInboundSession;
private static OlmInboundGroupSession mBobInboundGroupSession;
private static String mAliceOutboundSessionKey;
private static String mBobSessionIdentifier;
private static String mBobDecryptedMessage;
@ -53,7 +53,7 @@ public class OlmGroupTest {
public void test01CreateOutboundSession() {
// alice creates OUTBOUND GROUP SESSION
try {
mAliceOutboundSession = new OlmOutboundGroupSession();
mAliceOutboundGroupSession = new OlmOutboundGroupSession();
} catch (OlmException e) {
assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false);
}
@ -62,7 +62,7 @@ public class OlmGroupTest {
@Test
public void test02GetOutboundGroupSessionIdentifier() {
// test session ID
mAliceSessionIdentifier = mAliceOutboundSession.sessionIdentifier();
mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier();
assertNotNull(mAliceSessionIdentifier);
assertTrue(mAliceSessionIdentifier.length() > 0);
}
@ -70,7 +70,7 @@ public class OlmGroupTest {
@Test
public void test03GetOutboundGroupSessionKey() {
// test session Key
mAliceOutboundSessionKey = mAliceOutboundSession.sessionKey();
mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey();
assertNotNull(mAliceOutboundSessionKey);
assertTrue(mAliceOutboundSessionKey.length() > 0);
}
@ -78,56 +78,48 @@ public class OlmGroupTest {
@Test
public void test04GetOutboundGroupMessageIndex() {
// test message index before any encryption
mAliceMessageIndex = mAliceOutboundSession.messageIndex();
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
assertTrue(0 == mAliceMessageIndex);
}
@Test
public void test05OutboundGroupEncryptMessage() {
// alice encrypts a message to bob
mAliceToBobMessage = mAliceOutboundSession.encryptMessage(CLEAR_MESSAGE1);
mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1);
assertFalse(TextUtils.isEmpty(mAliceToBobMessage));
// test message index after encryption is incremented
mAliceMessageIndex = mAliceOutboundSession.messageIndex();
assertTrue(1== mAliceMessageIndex);
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
assertTrue(1 == mAliceMessageIndex);
}
@Test
public void test06CreateInboundGroupSession() {
// bob creates INBOUND GROUP SESSION with alice outbound key
try {
mBobInboundSession = new OlmInboundGroupSession(mAliceOutboundSessionKey);
mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey);
} catch (OlmException e) {
assertTrue("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false);
}
}
@Test
public void test07OutboundGroupSessionIdentifiers() {
// check session identifiers are equals
mAliceSessionIdentifier = mAliceOutboundSession.sessionIdentifier();
assertFalse(TextUtils.isEmpty(mAliceSessionIdentifier));
}
@Test
public void test08InboundGroupSessionIdentifiers() {
// check session identifiers are equals
mBobSessionIdentifier = mBobInboundSession.sessionIdentifier();
public void test08GetInboundGroupSessionIdentifier() {
// check both session identifiers are equals
mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier();
assertFalse(TextUtils.isEmpty(mBobSessionIdentifier));
assertTrue(mAliceSessionIdentifier.equals(mBobSessionIdentifier));
}
@Test
public void test09SessionIdentifiersIdentical() {
// check session identifiers are equals
public void test09SessionIdentifiersAreIdentical() {
// check both session identifiers are equals: alice vs bob
assertTrue(mAliceSessionIdentifier.equals(mBobSessionIdentifier));
}
@Test
public void test10InboundDecryptMessage() {
// test decrypted message
mBobDecryptedMessage = mBobInboundSession.decryptMessage(mAliceToBobMessage);
mBobDecryptedMessage = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage);
assertFalse(TextUtils.isEmpty(mBobDecryptedMessage));
assertTrue(mBobDecryptedMessage.equals(CLEAR_MESSAGE1));
}
@ -141,12 +133,12 @@ public class OlmGroupTest {
@Test
public void test12ReleaseOutboundSession() {
// release group sessions
mAliceOutboundSession.releaseSession();
mAliceOutboundGroupSession.releaseSession();
}
@Test
public void test13ReleaseInboundSession() {
// release group sessions
mBobInboundSession.releaseSession();
mBobInboundGroupSession.releaseSession();
}
}

View File

@ -250,5 +250,4 @@ public class OlmSessionTest {
// must be the same for both ends of the conversation
assertTrue(aliceSessionId.equals(bobSessionId));
}
}

View File

@ -0,0 +1,89 @@
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.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(){
// load native lib
mOlmManager = new OlmManager();
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;
String errorMsg = new String();
String message = "{\"key1\":\"value1\",\"key2\":\"value2\"};";
// create account
OlmAccount account = new OlmAccount();
assertNotNull(account);
// sign message
String messageSignature = account.signMessage(message);
assertNotNull(messageSignature);
// get identity key
JSONObject identityKeysJson = account.identityKeys();
assertNotNull(identityKeysJson);
try {
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);
}
// instance utility
OlmUtility utility = new OlmUtility();
boolean isVerified = utility.verifyEd25519Signature(messageSignature, fingerPrintKey, message, errorMsg);
assertTrue(isVerified);
utility.releaseUtility();
}
@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();
}
}

View File

@ -71,7 +71,6 @@ public class OlmAccount implements Serializable {
/**
* 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();

View File

@ -16,17 +16,33 @@
package org.matrix.olm;
/**
* Exception class to identify specific Olm SDk exceptions.
*/
public class OlmException extends Exception {
// exception codes
public static final int EXCEPTION_CODE_INIT_NEW_SESSION_FAILURE = 0;
public static final int EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION = 1;
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 2;
public static final int EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION = 0;
public static final int EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION = 1;
public static final int EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION = 2;
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 3;
// exception human readable messages
public static final String EXCEPTION_MSG_NEW_OUTBOUND_GROUP_SESSION = "failed to create a new outbound group Session";
public static final String EXCEPTION_MSG_NEW_INBOUND_GROUP_SESSION = "failed to create a new inbound group Session";
public static final String EXCEPTION_MSG_INIT_OUTBOUND_GROUP_SESSION = "failed to initialize a new outbound group Session";
public static final String EXCEPTION_MSG_INIT_INBOUND_GROUP_SESSION = "failed to initialize a new inbound group Session";
/** exception code to be taken from: {@link #EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION} {@link #EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION}
* {@link #EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION} {@link #EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION}**/
private final int mCode;
public OlmException(int aExceptionCode) {
/** Human readable message description **/
private final String mMessage;
public OlmException(int aExceptionCode, String aExceptionMessage) {
super();
mCode = aExceptionCode;
mMessage = aExceptionMessage;
}
public int getExceptionCode() {

View File

@ -45,26 +45,25 @@ public class OlmInboundGroupSession implements Serializable {
/**
* Constructor.<br>
* Create and save a new native session instance ID and start a new inbound group session.
* The session key parameter is retrieved from a outbound group session
* The session key parameter is retrieved from an outbound group session
* See {@link #initNewSession()} and {@link #initInboundGroupSessionWithSessionKey(String)}
* @param aSessionKey session key
* @throws OlmException
* @throws OlmException constructor failure
*/
public OlmInboundGroupSession(String aSessionKey) throws OlmException {
if(initNewSession()) {
if( 0 != initInboundGroupSessionWithSessionKey(aSessionKey)) {
releaseSession();// prevent memory leak before throwing
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION);
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION,OlmException.EXCEPTION_MSG_INIT_INBOUND_GROUP_SESSION);
}
} else {
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_NEW_SESSION_FAILURE);
throw new OlmException(OlmException.EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION, OlmException.EXCEPTION_MSG_NEW_INBOUND_GROUP_SESSION);
}
}
/**
* 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();
@ -81,8 +80,7 @@ public class OlmInboundGroupSession implements Serializable {
private native void releaseSessionJni();
/**
* Create and save the session native instance ID.
* Wrapper for {@link #initNewSessionJni()}.<br>
* Create and save the session native instance ID.<br>
* To be called before any other API call.
* @return true if init succeed, false otherwise.
*/

View File

@ -40,23 +40,22 @@ public class OlmOutboundGroupSession {
* Create and save a new session native instance ID and
* initialise a new outbound group session.<br>
* See {@link #initNewSession()} and {@link #initOutboundGroupSession()}
* @throws OlmException
* @throws OlmException constructor failure
*/
public OlmOutboundGroupSession() throws OlmException {
if(initNewSession()) {
if( 0 != initOutboundGroupSession()) {
releaseSession();// prevent memory leak before throwing
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION);
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION, OlmException.EXCEPTION_MSG_INIT_OUTBOUND_GROUP_SESSION);
}
} else {
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_NEW_SESSION_FAILURE);
throw new OlmException(OlmException.EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION, OlmException.EXCEPTION_MSG_NEW_OUTBOUND_GROUP_SESSION);
}
}
/**
* 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();

View File

@ -63,7 +63,6 @@ public class OlmSession implements Serializable {
/**
* 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();
@ -128,19 +127,19 @@ public class OlmSession implements Serializable {
* 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
* @param aPreKeyMsg PRE KEY message
* @return this if operation succeed, null otherwise
*/
public OlmSession initInboundSessionWithAccount(OlmAccount aAccount, String aOneTimeKeyMsg) {
public OlmSession initInboundSessionWithAccount(OlmAccount aAccount, String aPreKeyMsg) {
OlmSession retObj=null;
if((null==aAccount) || TextUtils.isEmpty(aOneTimeKeyMsg)){
if((null==aAccount) || TextUtils.isEmpty(aPreKeyMsg)){
Log.e(LOG_TAG, "## initInboundSessionWithAccount(): invalid input parameters");
} else {
// set the account of this session
mOlmAccount = aAccount;
if( 0 == initInboundSessionJni(mOlmAccount.getOlmAccountId(), aOneTimeKeyMsg)) {
if( 0 == initInboundSessionJni(mOlmAccount.getOlmAccountId(), aPreKeyMsg)) {
retObj = this;
}
}
@ -156,22 +155,23 @@ public class OlmSession implements Serializable {
* incoming PRE_KEY({@link OlmMessage#MESSAGE_TYPE_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).
* This method must only be called the first time a pre-key message is received from an inbound session.
* @param aAccount the account to associate with this session
* @param aTheirIdentityKey the sender identity key
* @param aOneTimeKeyMsg PRE KEY message
* @param aPreKeyMsg PRE KEY message
* @return this if operation succeed, null otherwise
* TODO unit test missing: initInboundSessionWithAccountFrom
*/
public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aOneTimeKeyMsg) {
public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aPreKeyMsg) {
OlmSession retObj=null;
if((null==aAccount) || TextUtils.isEmpty(aOneTimeKeyMsg)){
if((null==aAccount) || TextUtils.isEmpty(aPreKeyMsg)){
Log.e(LOG_TAG, "## initInboundSessionWithAccount(): invalid input parameters");
} else {
// set the account of this session
mOlmAccount = aAccount;
if(0 == initInboundSessionFromIdKeyJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aOneTimeKeyMsg)){
if(0 == initInboundSessionFromIdKeyJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aPreKeyMsg)){
retObj = this;
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright 2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import java.io.Serializable;
public class OlmUtility implements Serializable {
private static final String LOG_TAG = "OlmUtility";
/** raw pointer value returned by JNI.
* this value uniquely identifies this utility instance.
**/
private long mNativeOlmUtilityId;
public OlmUtility() {
initUtility();
}
/**
* Getter on the session ID.
* @return native session ID
*/
public long getOlmUtilityId(){
return mNativeOlmUtilityId;
}
/**
* Create a native utility instance.
* To be called before any other API call.
* @return true if init succeed, false otherwise.
*/
private boolean initUtility() {
boolean retCode = false;
if(0 != (mNativeOlmUtilityId = initUtilityJni())){
retCode = true;
}
return retCode;
}
private native long initUtilityJni();
/**
* Release native instance.<br>
* Public API for {@link #releaseUtilityJni()}.
*/
public void releaseUtility(){
releaseUtilityJni();
mNativeOlmUtilityId = 0;
}
private native void releaseUtilityJni();
/**
* Verify an ed25519 signature.<br>
* If the signature is verified, the method returns true. If false is returned, an error description is provided in aError.
* If the key was too small, aError is set to "OLM.INVALID_BASE64".
* If the signature was invalid, aError is set to "OLM.BAD_MESSAGE_MAC".<br>
* @param aSignature the base64-encoded message signature to be checked.
* @param aFingerprintKey the ed25519 key
* @param aMessage the message which was signed
* @param aError error message description
* @return true if the signature is verified, false otherwise
*/
public boolean verifyEd25519Signature(String aSignature, String aFingerprintKey, String aMessage, String aError) {
boolean retCode = false;
OlmUtility retObj=null;
if(null == aError) {
Log.e(LOG_TAG, "## verifyEd25519Signature(): invalid input error parameter");
}
else if(TextUtils.isEmpty(aSignature) || TextUtils.isEmpty(aFingerprintKey) || TextUtils.isEmpty(aMessage)){
Log.e(LOG_TAG, "## verifyEd25519Signature(): invalid input parameters");
} else {
String errorRetValue = verifyEd25519SignatureJni(aSignature,aFingerprintKey, aMessage);
if(null == errorRetValue) {
aError="";
retCode = true;
} else {
aError = errorRetValue;
}
}
return retCode;
}
private native String verifyEd25519SignatureJni(String aSignature, String aFingerprintKey, String aMessage);
/**
* Compute the hash(SHA-256) value of the string given in parameter(aMessageToHash).<br>
* The hash value is the returned by the method.
* @param aMessageToHash message to be hashed
* @return hash value if operation succeed, null otherwise
*/
public String sha256(String aMessageToHash) {
String hashRetValue = null;
if(null != aMessageToHash){
hashRetValue = sha256Jni(aMessageToHash);
}
return hashRetValue;
}
private native String sha256Jni(String aMessage);
// TODO missing API: initWithSerializedData
// TODO missing API: serializeDataWithKey
// TODO missing API: initWithCoder
// TODO missing API: encodeWithCoder
}