/*
* Copyright 2016 OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_session.h"
/**
* Init memory allocation for session creation.
* @return valid memory allocation, NULL otherwise
**/
OlmSession* initializeSessionMemory()
{
OlmSession* sessionPtr = NULL;
size_t sessionSize = olm_session_size();
if(NULL != (sessionPtr=(OlmSession*)malloc(sessionSize)))
{ // init session object
sessionPtr = olm_session(sessionPtr);
LOGD("## initializeSessionMemory(): success - OLM session size=%lu",sessionSize);
}
else
{
LOGE("## initializeSessionMemory(): failure - OOM");
}
return sessionPtr;
}
JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv *env, jobject thiz)
{
OlmSession* sessionPtr = NULL;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
}
else
{ // even if free(NULL) does not crash, a test is performed for debug purpose
LOGD("## releaseSessionJni(): IN");
free(sessionPtr);
LOGD("## releaseSessionJni(): OUT");
}
}
/**
* Initialize a new session and return it to JAVA side.
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmSession* => jlong) platform independent.
* @return the initialized OlmSession* instance if init succeed, NULL otherwise
**/
JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmSession_initNewSessionJni(JNIEnv *env, jobject thiz)
{
OlmSession* sessionPtr = NULL;
// init account memory allocation
if(NULL == (sessionPtr = initializeSessionMemory()))
{
LOGE("## initNewSessionJni(): failure - init session OOM");
}
else
{
LOGD("## initNewSessionJni(): success - OLM session created");
}
return (jlong)(intptr_t)sessionPtr;
}
// *********************************************************************
// ********************** OUTBOUND SESSION *****************************
// *********************************************************************
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message.
The recipient is defined as the entity
* with whom the session is established.
* @param aOlmAccountId account instance
* @param aTheirIdentityKey the identity key of the recipient
* @param aTheirOneTimeKey the one time key of the recipient
* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
**/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey)
{
jint retCode = ERROR_CODE_KO;
OlmSession* sessionPtr = NULL;
OlmAccount* accountPtr = NULL;
void *randomBuffPtr;
size_t sessionResult;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## initOutboundSessionJni(): failure - invalid Session ptr=NULL");
}
else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId))
{
LOGE("## initOutboundSessionJni(): failure - invalid Account ptr=NULL");
}
else if((0==aTheirIdentityKey) || (0==aTheirOneTimeKey))
{
LOGE("## initOutboundSessionJni(): failure - invalid keys");
}
else
{ // allocate random buffer
size_t randomSize = olm_create_outbound_session_random_length(sessionPtr);
if(NULL == (randomBuffPtr = (void*)malloc(randomSize*sizeof(void*))))
{
LOGE("## initOutboundSessionJni(): failure - random buffer OOM");
}
else
{ // convert identity & one time keys to C strings
const char* theirIdentityKeyPtr = NULL;
const char* theirOneTimeKeyPtr = NULL;
if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
{
LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM");
}
else if(NULL == (theirOneTimeKeyPtr = env->GetStringUTFChars(aTheirOneTimeKey, 0)))
{
LOGE("## initOutboundSessionJni(): failure - one time Key JNI allocation OOM");
}
else
{
int theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey);
int theirOneTimeKeyLength = env->GetStringUTFLength(aTheirOneTimeKey);
LOGD("## initOutboundSessionJni(): identityKey=%s oneTimeKey=%s",theirIdentityKeyPtr,theirOneTimeKeyPtr);
sessionResult = olm_create_outbound_session(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, theirOneTimeKeyPtr, theirOneTimeKeyLength, randomBuffPtr, randomSize);
if(sessionResult == olm_error()) {
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## initOutboundSessionJni(): failure - session creation Msg=%s",errorMsgPtr);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## initOutboundSessionJni(): success - result=%ld", sessionResult);
}
}
// free local alloc
free(randomBuffPtr);
if(NULL!= theirIdentityKeyPtr)
{
env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
}
if(NULL!= theirOneTimeKeyPtr)
{
env->ReleaseStringUTFChars(aTheirOneTimeKey, theirOneTimeKeyPtr);
}
}
}
return retCode;
}
// *********************************************************************
// *********************** INBOUND SESSION *****************************
// *********************************************************************
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message.
* @param aOlmAccountId account instance
* @param aOneTimeKeyMsg PRE_KEY message TODO TBC
* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg)
{
jint retCode = ERROR_CODE_KO;
OlmSession *sessionPtr = NULL;
OlmAccount *accountPtr = NULL;
size_t sessionResult;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## initInboundSessionJni(): failure - invalid Session ptr=NULL");
}
else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId))
{
LOGE("## initInboundSessionJni(): failure - invalid Account ptr=NULL");
}
else if(0==aOneTimeKeyMsg)
{
LOGE("## initOutboundSessionJni(): failure - invalid message");
}
else
{ // convert message to C strings
const char *messagePtr = NULL;
if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
{
LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM");
}
else
{
int messageLength = env->GetStringUTFLength(aOneTimeKeyMsg);
LOGD("## initInboundSessionJni(): message=%s messageLength=%d",messagePtr,messageLength);
sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength);
if(sessionResult == olm_error()) {
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## initInboundSessionJni(): failure - init inbound session creation Msg=%s",errorMsgPtr);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## initInboundSessionJni(): success - result=%ld", sessionResult);
}
// free local alloc
env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
}
}
return retCode;
}
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message based on the recipient identity key.
* @param aOlmAccountId account instance
* @param aTheirIdentityKey the identity key of the recipient
* @param aOneTimeKeyMsg encrypted message
* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg)
{
jint retCode = ERROR_CODE_KO;
OlmSession *sessionPtr = NULL;
OlmAccount *accountPtr = NULL;
const char *messagePtr = NULL;
const char *theirIdentityKeyPtr = NULL;
size_t sessionResult;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL");
}
else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Account ptr=NULL");
}
else if(0 == aTheirIdentityKey)
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey");
}
else if(0==aOneTimeKeyMsg)
{
LOGE("## initOutboundSessionJni(): failure - invalid one time key message");
}
else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - message JNI allocation OOM");
}
else if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM");
}
else
{
size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg);
size_t theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey);
LOGD("## initInboundSessionFromIdKeyJni(): message=%s messageLength=%lu",messagePtr,messageLength);
sessionResult = olm_create_inbound_session_from(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, (void*)messagePtr , messageLength);
if(sessionResult == olm_error()) {
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation Msg=%s",errorMsgPtr);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## initInboundSessionFromIdKeyJni(): success - result=%ld", sessionResult);
}
}
// free local alloc
if(NULL!= messagePtr)
{
env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
}
if(NULL!= theirIdentityKeyPtr)
{
env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
}
return retCode;
}
/**
* Checks if the PRE_KEY message is for this in-bound session.
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* @param aOneTimeKeyMsg PRE KEY message
* @return ERROR_CODE_OK if match, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(JNIEnv *env, jobject thiz, jstring aOneTimeKeyMsg)
{
jint retCode = ERROR_CODE_KO;
OlmSession *sessionPtr = NULL;
const char *messagePtr = NULL;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## matchesInboundSessionJni(): failure - invalid Session ptr=NULL");
}
else if(0==aOneTimeKeyMsg)
{
LOGE("## matchesInboundSessionJni(): failure - invalid one time key message");
}
else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
{
LOGE("## matchesInboundSessionJni(): failure - one time key JNI allocation OOM");
}
else
{
size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg);
size_t matchResult = olm_matches_inbound_session(sessionPtr, (void*)messagePtr , messageLength);
if(matchResult == olm_error()) {
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## matchesInboundSessionJni(): failure - no match Msg=%s",errorMsgPtr);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## matchesInboundSessionJni(): success - result=%ld", matchResult);
}
}
return retCode;
}
/**
* Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* @param aTheirIdentityKey the identity key of the sender
* @param aOneTimeKeyMsg PRE KEY message
* @return ERROR_CODE_OK if match, ERROR_CODE_KO otherwise
*/
JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg)
{
jint retCode = ERROR_CODE_KO;
OlmSession *sessionPtr = NULL;
const char *messagePtr = NULL;
const char *theirIdentityKeyPtr = NULL;
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL");
}
else if(0 == aTheirIdentityKey)
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey");
}
else if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM");
}
else if(0==aOneTimeKeyMsg)
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid one time key message");
}
else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - one time key JNI allocation OOM");
}
else
{
size_t identityKeyLength = env->GetStringUTFLength(aTheirIdentityKey);
size_t messageLength = env->GetStringUTFLength(aOneTimeKeyMsg);
size_t matchResult = olm_matches_inbound_session_from(sessionPtr, (void const *)theirIdentityKeyPtr, identityKeyLength, (void*)messagePtr , messageLength);
if(matchResult == olm_error()) {
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - no match Msg=%s",errorMsgPtr);
}
else
{
retCode = ERROR_CODE_OK;
LOGD("## matchesInboundSessionFromIdKeyJni(): success - result=%lu", matchResult);
}
}
return retCode;
}
/**
* Get the session identifier for this session.
* @return the session identifier if operation succeed, null otherwise
*/
JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz)
{
OlmSession *sessionPtr = NULL;
void *sessionIdPtr = NULL;
jstring returnValueStr=0;
// get the size to alloc to contain the id
size_t lengthSessId = olm_session_id_length(sessionPtr);
if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
{
LOGE("## getSessionIdentifierJni(): failure - invalid Session ptr=NULL");
}
else if(NULL == (sessionIdPtr = (void*)malloc(lengthSessId*sizeof(void*))))
{
LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM");
}
else
{
size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessId);
if (result == olm_error())
{
const char *errorMsgPtr = olm_session_last_error(sessionPtr);
LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr);
}
else
{
returnValueStr = env->NewStringUTF((const char*)sessionIdPtr);
}
free(sessionIdPtr);
}
return returnValueStr;
}
/**
* Read the account instance ID of the calling object (aJavaObject) passed in parameter.
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @return the instance ID if read succeed, -1 otherwise.
**/
jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
jlong instanceId=-1;
jfieldID instanceIdField;
jclass loaderClass;
if(NULL!=aJniEnv)
{
if(0 != (loaderClass=aJniEnv->GetObjectClass(aJavaObject)))
{
if(0 != (instanceIdField=aJniEnv->GetFieldID(loaderClass, "mNativeOlmSessionId", "J")))
{
instanceId = aJniEnv->GetIntField(aJavaObject, instanceIdField);
aJniEnv->DeleteLocalRef(loaderClass);
}
else
{
LOGD("## getSessionInstanceId() ERROR! GetFieldID=null");
}
}
else
{
LOGD("## getSessionInstanceId() ERROR! GetObjectClass=null");
}
}
else
{
LOGD("## getSessionInstanceId() ERROR! aJniEnv=NULL");
}
LOGD("## getSessionInstanceId() success - instanceId=%lld",instanceId);
return instanceId;
}