Commit 655c841c authored by pedroGitt's avatar pedroGitt
Browse files

- Update Unit tests for OlmAccount

 - new file olm_utility.cpp to have a stand alone function to initialize/alloc a random buffer
 - new class OlmMessage
 - complete OlmSession API with encryptMessage()
 - comments review
 - OlmAccount unit tests are green
 - new gradle to compile the shared lib according to debug mode
parent 0393ad68
......@@ -24,9 +24,16 @@ android {
jni.srcDirs = []
}
task ndkBuildNative(type: Exec, description: 'NDK building..') {
task ndkBuildNativeRelease(type: Exec, description: 'NDK building..') {
println 'ndkBuildNativeRelease starts..'
workingDir file('src/main')
commandLine getNdkBuildCmd() //, '-B', 'NDK_DEBUG=1'
commandLine getNdkBuildCmd(), 'NDK_DEBUG=0'
}
task ndkBuildNativeDebug(type: Exec, description: 'NDK building..') {
println 'ndkBuildNativeDebug starts..'
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean NDK build') {
......@@ -34,10 +41,21 @@ android {
commandLine getNdkBuildCmd(), 'clean'
}
tasks.withType(JavaCompile) {
/*tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuildNative
}*/
tasks.withType(JavaCompile) {
compileTask -> if (compileTask.name.startsWith('compileDebugJava')) {
println 'test compile: Debug'
compileTask.dependsOn ndkBuildNativeDebug
} else if (compileTask.name.startsWith('compileReleaseJava')) {
println 'test compile: Release'
compileTask.dependsOn ndkBuildNativeRelease
}
}
clean.dependsOn cleanNative
}
......
......@@ -55,7 +55,6 @@ public class OlmAccountTest {
}
}
@After
public void tearDown() {
// TBD
......@@ -63,30 +62,25 @@ public class OlmAccountTest {
@Test
public void test1CreateAccount() {
Log.d(LOG_TAG,"## testInitNewAccount");
mOlmAccount = new OlmAccount();
assertNotNull(mOlmAccount);
}
@Test
public void test2InitNewAccount() {
Log.d(LOG_TAG,"## testInitNewAccount");
assertTrue(mOlmAccount.initNewAccount());
mIsAccountCreated = true;
}
@Test
public void test3GetOlmAccountId() {
Log.d(LOG_TAG,"## testGetOlmAccountId");
long olmNativeInstance = mOlmAccount.getOlmAccountId();
Log.d(LOG_TAG,"## testGetOlmAccountId olmNativeInstance="+olmNativeInstance);
assertTrue(0!=olmNativeInstance);
}
@Test
public void test4IdentityKeys() {
Log.d(LOG_TAG,"## testIdentityKeys");
JSONObject identityKeysJson = mOlmAccount.identityKeys();
assertNotNull(identityKeysJson);
Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson);
......@@ -115,8 +109,6 @@ public class OlmAccountTest {
//****************************************************
@Test
public void test5MaxOneTimeKeys() {
Log.d(LOG_TAG,"## testMaxOneTimeKeys");
long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys();
Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys);
......@@ -125,14 +117,12 @@ public class OlmAccountTest {
@Test
public void test6GenerateOneTimeKeys() {
Log.d(LOG_TAG,"## testGenerateOneTimeKeys");
int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
assertTrue(0==retValue);
}
@Test
public void test7OneTimeKeysJsonFormat() {
Log.d(LOG_TAG,"## test7OneTimeKeysJsonFormat");
int oneTimeKeysCount = 0;
JSONObject generatedKeysJsonObj;
JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys();
......@@ -169,8 +159,6 @@ public class OlmAccountTest {
@Test
public void test8MarkOneTimeKeysAsPublished() {
Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished");
int retCode = mOlmAccount.markOneTimeKeysAsPublished();
// if OK => retCode=0
assertTrue(0 == retCode);
......@@ -178,8 +166,6 @@ public class OlmAccountTest {
@Test
public void test9SignMessage() {
Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished");
String clearMsg = "String to be signed by olm";
String signedMsg = mOlmAccount.signMessage(clearMsg);
assertNotNull(signedMsg);
......
/*
* 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;
}
......@@ -146,12 +146,12 @@ public class OlmSession {
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message based on the sender identity key TODO TBC!.<br>
* 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 TODO TBC
* @param aOneTimeKeyMsg PRE KEY message
* @return this if operation succeed, null otherwise
*/
public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aOneTimeKeyMsg) {
......@@ -173,6 +173,18 @@ public class OlmSession {
private native int initInboundSessionFromIdKeyJni(long aOlmAccountId, String aTheirIdentityKey, String aOneTimeKeyMsg);
/**
* Get the session identifier.<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>
......@@ -194,129 +206,43 @@ public class OlmSession {
/**
* 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();
/*
- (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage;
- (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage;
// UTF-8 plaintext -> base64 ciphertext
- (OLMMessage*) encryptMessage:(NSString*)message;
// base64 ciphertext -> UTF-8 plaintext
- (NSString*) decryptMessage:(OLMMessage*)message;
*/
/**
* Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).<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
* 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
*/
private native byte[] identityKeysJni();
public boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) {
boolean retCode = false;
/**
* Return the identity keys in a JSON array.<br>
* Public API for {@link #identityKeysJni()}.
* @return identity keys in JSON array format if operation succeed, null otherwise
*/
public JSONObject identityKeys() {
JSONObject identityKeysJsonObj = null;
byte identityKeysBuffer[];
if( null != (identityKeysBuffer = identityKeysJni())) {
try {
identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer));
Log.d(LOG_TAG, "## identityKeys(): Identity Json keys=" + identityKeysJsonObj.toString());
} catch (JSONException e) {
identityKeysJsonObj = null;
Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage());
}
} else {
Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null");
if(0 == matchesInboundSessionFromIdKeyJni(aTheirIdentityKey, aOneTimeKeyMsg)){
retCode = true;
}
return identityKeysJsonObj;
return retCode;
}
/**
* Return the largest number of "one time keys" this account can store.
* @return the max number of "one time keys", -1 otherwise
*/
public native long maxOneTimeKeys();
private native int matchesInboundSessionFromIdKeyJni(String aTheirIdentityKey, String aOneTimeKeyMsg);
/**
* 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.
* @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.
* These keys must be published on the server.
* @return byte array containing the one time keys if operation succeed, null otherwise
* Encrypt a message using the session.<br>
* Public API for {@link #encryptMessageJni(String, OlmMessage)}.
* @param aClearMsg message to encrypted
* @return the encrypted message if operation succeed, null otherwise
*/
private native byte[] oneTimeKeysJni();
public OlmMessage encryptMessage(String aClearMsg) {
OlmMessage encryptedMsgRetValue = new OlmMessage();
/**
* Return the "one time keys" in a JSON array.<br>
* Public API for {@link #oneTimeKeysJni()}.
* @return one time keys in JSON array format if operation succeed, null otherwise
*/
public JSONObject oneTimeKeys() {
byte identityKeysBuffer[];
JSONObject identityKeysJsonObj = null;
if( null != (identityKeysBuffer = oneTimeKeysJni())) {
try {
identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer));
Log.d(LOG_TAG, "## oneTimeKeys(): Identity Json keys=" + identityKeysJsonObj.toString());
} catch (JSONException e) {
identityKeysJsonObj = null;
Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage());
}
} else {
Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null");
if(0 != encryptMessageJni(aClearMsg, encryptedMsgRetValue)){
encryptedMsgRetValue = null;
}
return identityKeysJsonObj;
return encryptedMsgRetValue;
}
/**
* 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);
private native int encryptMessageJni(String aClearMsg, OlmMessage aEncryptedMsg);
/**
* Marks the current set of "one time keys" as being published.
* @return 0 if operation succeed, -1 otherwise
*/
public native int markOneTimeKeysAsPublished();
/**
* Sign a message with the ed25519 fingerprint key for this account.
* @param aMessage message to sign
* @return the signed message if operation succeed, null otherwise
*/
public native String signMessage(String aMessage);
@Override
public String toString() {
......
......@@ -44,7 +44,8 @@ $(SRC_ROOT_DIR)/lib/crypto-algorithms/sha256.c \
$(SRC_ROOT_DIR)/lib/crypto-algorithms/aes.c \
$(SRC_ROOT_DIR)/lib/curve25519-donna/curve25519-donna.c \
olm_account.cpp \
olm_session.cpp
olm_session.cpp \
olm_utility.cpp
LOCAL_LDLIBS := -llog
......
......@@ -15,7 +15,7 @@
*/
#include "olm_account.h"
#include "olm_utility.h"
/**
* Init memory allocation for account creation.
......@@ -70,10 +70,10 @@ JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv *
**/
JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv *env, jobject thiz)
{
OlmAccount* accountPtr = NULL;
OlmAccount *accountPtr = NULL;
uint8_t *randomBuffPtr = NULL;
size_t accountRetCode;
uint8_t* randomBuffPtr = NULL;
int randomSize;
size_t randomSize;
// init account memory allocation
if(NULL == (accountPtr = initializeAccountMemory()))
......@@ -84,36 +84,34 @@ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv
{
// allocate random buffer
randomSize = olm_create_account_random_length(accountPtr);
if(NULL == (randomBuffPtr = (std::uint8_t*)malloc(randomSize*sizeof(std::uint8_t))))
if(false == setRandomInBuffer(&randomBuffPtr, randomSize))
{
LOGE("## initNewAccount(): failure - random buffer OOM");
LOGE("## initNewAccount(): failure - random buffer init");
}
else
{ // create random buffer
LOGD("## initNewAccount(): randomSize=%d",randomSize);
srand(time(NULL)); // init seed
for(int i=0;i<randomSize;i++)
{
randomBuffPtr[i] = (std::uint8_t)(rand()%ACCOUNT_CREATION_RANDOM_MODULO);;
}
{
// create account
accountRetCode = olm_create_account(accountPtr, randomBuffPtr, randomSize);
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);
}
free(randomBuffPtr);
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 *************************
// *********************************************************************
......@@ -202,10 +200,10 @@ JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_maxOneTimeKeys(JNIEnv *en
**/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv *env, jobject thiz, jint aNumberOfKeys)
{
OlmAccount* accountPtr = NULL;;
OlmAccount *accountPtr = NULL;
uint8_t *randomBufferPtr = NULL;
jint retCode = ERROR_CODE_KO;
size_t length;
void* keysBytesPtr; // TODO check type: or uint8_t?
size_t randomLength;
size_t result;
LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys);
......@@ -216,15 +214,16 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv
}
else
{ // keys memory allocation
length = olm_account_generate_one_time_keys_random_length(accountPtr, aNumberOfKeys);
LOGD("## generateOneTimeKeys(): randomLength=%ld", length);
if(NULL == (keysBytesPtr=(void*)malloc(length*sizeof(void*))))
randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, aNumberOfKeys);
LOGD("## generateOneTimeKeys(): randomLength=%ld", randomLength);
if(false == setRandomInBuffer(&randomBufferPtr, randomLength))
{
LOGE("## generateOneTimeKeys(): failure - random allocation OOM");
LOGE("## generateOneTimeKeys(): failure - random buffer init");
}
else
{ // retrieve key pairs in keysBytesPtr
result = olm_account_generate_one_time_keys(accountPtr, aNumberOfKeys, keysBytesPtr, length);
result = olm_account_generate_one_time_keys(accountPtr, 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);
......@@ -234,11 +233,14 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv
retCode = ERROR_CODE_OK;
LOGD("## generateOneTimeKeys(): success - result=%ld", result);
}
free(keysBytesPtr);
}
}
if(NULL != randomBufferPtr)
{
free(randomBufferPtr);
}
return retCode;
}
......
......@@ -34,7 +34,7 @@ static const int ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS = ERROR_CODE_OK+1;
static const int ERROR_CODE_KO = -1;
// constants
static const int ACCOUNT_CREATION_RANDOM_MODULO = 500;
static const int ACCOUNT_CREATION_RANDOM_MODULO = 256;
typedef struct _AccountContext
......
......@@ -15,6 +15,7 @@
*/
#include "olm_session.h"
#include "olm_utility.h"
/**
......@@ -95,7 +96,9 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI
jint retCode = ERROR_CODE_KO;
OlmSession* sessionPtr = NULL;
OlmAccount* accountPtr = NULL;
void *randomBuffPtr;
const char* theirIdentityKeyPtr = NULL;
const char* theirOneTimeKeyPtr = NULL;
uint8_t *randomBuffPtr = NULL;
size_t sessionResult;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
......@@ -113,15 +116,12 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI
else
{ // allocate random buffer
size_t randomSize = olm_create_outbound_session_random_length(sessionPtr);
if(NULL == (randomBuffPtr = (void*)malloc(randomSize*sizeof(void*))))
if(false == setRandomInBuffer(&randomBuffPtr, randomSize))
{
LOGE("## initOutboundSessionJni(): failure - random buffer OOM");
LOGE("## initOutboundSessionJni(): failure - random buffer init");
}
else
{ // convert identity & one time keys to C strings
const char* theirIdentityKeyPtr = NULL;
const char* theirOneTimeKeyPtr = NULL;
if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
{
LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM");
......@@ -136,7 +136,14 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI
int theirOneTimeKeyLength = env->GetStringUTFLength(aTheirOneTimeKey);
LOGD("## initOutboundSessionJni(): identityKey=%s oneTimeKey=%s",theirIdentityKeyPtr,theirOneTimeKeyPtr);
sessionResult = olm_create_outbound_session(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, theirOneTimeKeyPtr, theirOneTimeKeyLength, randomBuffPtr, randomSize);
sessionResult = olm_create_outbound_session(sessionPtr,
accountPtr,
theirIdentityKeyPtr,
theirIdentityKeyLength,
theirOneTimeKeyPtr,
theirOneTimeKeyLength,
(void*)randomBuffPtr,
randomSize);
if(sessionResult == olm_error()) {
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## initOutboundSessionJni(): failure - session creation Msg=%s",errorMsgPtr);
......@@ -147,20 +154,25 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI
LOGD("## initOutboundSessionJni(): success - result=%ld", sessionResult);
}
}
// free local alloc
free(randomBuffPtr);
if(NULL!= theirIdentityKeyPtr)
{
env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
}
if(NULL!= theirOneTimeKeyPtr)
{
env->ReleaseStringUTFChars(aTheirOneTimeKey, theirOneTimeKeyPtr);
}
}
}
// **** free mem alloc ***
if(NULL!= randomBuffPtr)
{
free(randomBuffPtr);
}
if(NULL!= theirIdentityKeyPtr)
{
env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
}
if(NULL!= theirOneTimeKeyPtr)
{
env->ReleaseStringUTFChars(aTheirOneTimeKey, theirOneTimeKeyPtr);
}
return retCode;
}
......@@ -172,7 +184,7 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNI
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message.<br>
* @param aOlmAccountId account instance
* @param aOneTimeKeyMsg PRE_KEY message TODO TBC
* @param aOneTimeKeyMsg PRE_KEY message
* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg)
......@@ -337,6 +349,12 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(J
}
}
// free local alloc
if(NULL!= messagePtr)
{
env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
}
return retCode;
}
......@@ -392,10 +410,128 @@ JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromI