/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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"
using namespace AndroidOlmSdk;
/**
* Init memory allocation for a session creation.
* Make sure releaseSessionJni() is called when one is done with the session instance.
* @return valid memory allocation, NULL otherwise
**/
OlmSession* initializeSessionMemory()
{
size_t sessionSize = olm_session_size();
OlmSession* sessionPtr = (OlmSession*)malloc(sessionSize);
if (sessionPtr)
{
// init session object
sessionPtr = olm_session(sessionPtr);
LOGD("## initializeSessionMemory(): success - OLM session size=%lu",static_cast(sessionSize));
}
else
{
LOGE("## initializeSessionMemory(): failure - OOM");
}
return sessionPtr;
}
JNIEXPORT jlong OLM_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz)
{
LOGD("## createNewSessionJni(): IN");
OlmSession* accountPtr = initializeSessionMemory();
if (!accountPtr)
{
LOGE("## initNewAccount(): failure - init session OOM");
env->ThrowNew(env->FindClass("java/lang/Exception"), "init session OOM");
}
else
{
LOGD(" ## createNewSessionJni(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr);
}
return (jlong)(intptr_t)accountPtr;
}
JNIEXPORT void OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releaseSessionJni(): IN");
OlmSession* sessionPtr = getSessionInstanceId(env, thiz);
if (!sessionPtr)
{
LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
}
else
{
olm_clear_session(sessionPtr);
// even if free(NULL) does not crash, logs are performed for debug purpose
free(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 or an exception is thrown
**/
JNIEXPORT void OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKeyBuffer, jbyteArray aTheirOneTimeKeyBuffer)
{
OlmSession* sessionPtr = getSessionInstanceId(env, thiz);
const char* errorMessage = NULL;
OlmAccount* accountPtr = NULL;
if (!sessionPtr)
{
LOGE("## initOutboundSessionJni(): failure - invalid Session ptr=NULL");
errorMessage = "invalid Session ptr=NULL";
}
else if (!(accountPtr = (OlmAccount*)aOlmAccountId))
{
LOGE("## initOutboundSessionJni(): failure - invalid Account ptr=NULL");
errorMessage = "invalid Account ptr=NULL";
}
else if (!aTheirIdentityKeyBuffer || !aTheirOneTimeKeyBuffer)
{
LOGE("## initOutboundSessionJni(): failure - invalid keys");
errorMessage = "invalid keys";
}
else
{
size_t randomSize = olm_create_outbound_session_random_length(sessionPtr);
uint8_t *randomBuffPtr = NULL;
LOGD("## initOutboundSessionJni(): randomSize=%lu",static_cast(randomSize));
if ( (0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize))
{
LOGE("## initOutboundSessionJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
jbyte* theirIdentityKeyPtr = NULL;
jbyte* theirOneTimeKeyPtr = NULL;
// convert identity & one time keys to C strings
if (!(theirIdentityKeyPtr = env->GetByteArrayElements(aTheirIdentityKeyBuffer, 0)))
{
LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM");
errorMessage = "identityKey JNI allocation OOM";
}
else if (!(theirOneTimeKeyPtr = env->GetByteArrayElements(aTheirOneTimeKeyBuffer, 0)))
{
LOGE("## initOutboundSessionJni(): failure - one time Key JNI allocation OOM");
errorMessage = "one time Key JNI allocation OOM";
}
else
{
size_t theirIdentityKeyLength = (size_t)env->GetArrayLength(aTheirIdentityKeyBuffer);
size_t theirOneTimeKeyLength = (size_t)env->GetArrayLength(aTheirOneTimeKeyBuffer);
LOGD("## initOutboundSessionJni(): identityKey=%.*s oneTimeKey=%.*s", static_cast(theirIdentityKeyLength), theirIdentityKeyPtr, static_cast(theirOneTimeKeyLength), theirOneTimeKeyPtr);
size_t sessionResult = olm_create_outbound_session(sessionPtr,
accountPtr,
theirIdentityKeyPtr,
theirIdentityKeyLength,
theirOneTimeKeyPtr,
theirOneTimeKeyLength,
(void*)randomBuffPtr,
randomSize);
if (sessionResult == olm_error()) {
errorMessage = (const char *)olm_session_last_error(sessionPtr);
LOGE("## initOutboundSessionJni(): failure - session creation Msg=%s", errorMessage);
}
else
{
LOGD("## initOutboundSessionJni(): success - result=%lu", static_cast(sessionResult));
}
}
if (theirIdentityKeyPtr)
{
env->ReleaseByteArrayElements(aTheirIdentityKeyBuffer, theirIdentityKeyPtr, JNI_ABORT);
}
if (theirOneTimeKeyPtr)
{
env->ReleaseByteArrayElements(aTheirOneTimeKeyBuffer, theirOneTimeKeyPtr, JNI_ABORT);
}
if (randomBuffPtr)
{
memset(randomBuffPtr, 0, randomSize);
free(randomBuffPtr);
}
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
// *********************************************************************
// *********************** INBOUND SESSION *****************************
// *********************************************************************
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message.
* An exception is thrown if the operation fails.
* @param aOlmAccountId account instance
* @param aOneTimeKeyMsg PRE_KEY message
*/
JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aOneTimeKeyMsgBuffer)
{
const char* errorMessage = NULL;
OlmSession *sessionPtr = getSessionInstanceId(env,thiz);
OlmAccount *accountPtr = NULL;
size_t sessionResult;
if (!sessionPtr)
{
LOGE("## initInboundSessionJni(): failure - invalid Session ptr=NULL");
errorMessage = "invalid Session ptr=NULL";
}
else if (!(accountPtr = (OlmAccount*)aOlmAccountId))
{
LOGE("## initInboundSessionJni(): failure - invalid Account ptr=NULL");
errorMessage = "invalid Account ptr=NULL";
}
else if (!aOneTimeKeyMsgBuffer)
{
LOGE("## initInboundSessionJni(): failure - invalid message");
errorMessage = "invalid message";
}
else
{
jbyte* messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0);
if (!messagePtr)
{
LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM");
errorMessage = "message JNI allocation OOM";
}
else
{
size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer);
LOGD("## initInboundSessionJni(): messageLength=%lu message=%.*s", static_cast(messageLength), static_cast(messageLength), messagePtr);
sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength);
if (sessionResult == olm_error())
{
errorMessage = olm_session_last_error(sessionPtr);
LOGE("## initInboundSessionJni(): failure - init inbound session creation Msg=%s", errorMessage);
}
else
{
LOGD("## initInboundSessionJni(): success - result=%lu", static_cast(sessionResult));
}
// free local alloc
env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message based on the recipient identity key.
* An exception is thrown if the operation fails.
* @param aOlmAccountId account instance
* @param aTheirIdentityKey the identity key of the recipient
* @param aOneTimeKeyMsg encrypted message
*/
JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKeyBuffer, jbyteArray aOneTimeKeyMsgBuffer)
{
const char* errorMessage = NULL;
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
OlmAccount *accountPtr = NULL;
jbyte *messagePtr = NULL;
jbyte *theirIdentityKeyPtr = NULL;
size_t sessionResult;
if (!sessionPtr)
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL");
errorMessage = "invalid Session ptr=NULL";
}
else if (!(accountPtr = (OlmAccount*)aOlmAccountId))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Account ptr=NULL");
errorMessage = "invalid Account ptr=NULL";
}
else if (!aTheirIdentityKeyBuffer)
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey");
errorMessage = "invalid theirIdentityKey";
}
else if (!aOneTimeKeyMsgBuffer)
{
LOGE("## initInboundSessionJni(): failure - invalid one time key message");
errorMessage = "invalid invalid one time key message";
}
else if (!(messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0)))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - message JNI allocation OOM");
errorMessage = "message JNI allocation OOM";
}
else if(!(theirIdentityKeyPtr = env->GetByteArrayElements(aTheirIdentityKeyBuffer, 0)))
{
LOGE("## initInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM");
errorMessage = "theirIdentityKey JNI allocation OOM";
}
else
{
size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer);
size_t theirIdentityKeyLength = (size_t)env->GetArrayLength(aTheirIdentityKeyBuffer);
LOGD("## initInboundSessionFromIdKeyJni(): message=%.*s messageLength=%lu", static_cast(messageLength), messagePtr, static_cast(messageLength));
sessionResult = olm_create_inbound_session_from(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, (void*)messagePtr , messageLength);
if (sessionResult == olm_error())
{
errorMessage = (const char *)olm_session_last_error(sessionPtr);
LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation Msg=%s", errorMessage);
}
else
{
LOGD("## initInboundSessionFromIdKeyJni(): success - result=%lu", static_cast(sessionResult));
}
}
// free local alloc
if (messagePtr)
{
env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT);
}
if (theirIdentityKeyPtr)
{
env->ReleaseByteArrayElements(aTheirIdentityKeyBuffer, theirIdentityKeyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* 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 true if the PRE_KEY message matches
*/
JNIEXPORT jboolean OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aOneTimeKeyMsgBuffer)
{
jboolean retCode = JNI_FALSE;
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
jbyte *messagePtr = NULL;
if (!sessionPtr)
{
LOGE("## matchesInboundSessionJni(): failure - invalid Session ptr=NULL");
}
else if (!aOneTimeKeyMsgBuffer)
{
LOGE("## matchesInboundSessionJni(): failure - invalid one time key message");
}
else if (!(messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0)))
{
LOGE("## matchesInboundSessionJni(): failure - one time key JNI allocation OOM");
}
else
{
size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer);
size_t matchResult = olm_matches_inbound_session(sessionPtr, (void*)messagePtr , messageLength);
//if(matchResult == olm_error()) {
// for now olm_matches_inbound_session() returns 1 when it succeeds, otherwise 1- or 0
if (matchResult != 1) {
LOGE("## matchesInboundSessionJni(): failure - no match Msg=%s",(const char *)olm_session_last_error(sessionPtr));
}
else
{
retCode = JNI_TRUE;
LOGD("## matchesInboundSessionJni(): success - result=%lu", static_cast(matchResult));
}
}
// free local alloc
if (messagePtr)
{
env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT);
}
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 true if the PRE_KEY message matches.
*/
JNIEXPORT jboolean JNICALL OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aTheirIdentityKeyBuffer, jbyteArray aOneTimeKeyMsgBuffer)
{
jboolean retCode = JNI_FALSE;
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
jbyte *messagePtr = NULL;
jbyte *theirIdentityKeyPtr = NULL;
if (!sessionPtr)
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL");
}
else if (!aTheirIdentityKeyBuffer)
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey");
}
else if (!(theirIdentityKeyPtr = env->GetByteArrayElements(aTheirIdentityKeyBuffer, 0)))
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM");
}
else if (!aOneTimeKeyMsgBuffer)
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid one time key message");
}
else if (!(messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0)))
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - one time key JNI allocation OOM");
}
else
{
size_t identityKeyLength = (size_t)env->GetArrayLength(aTheirIdentityKeyBuffer);
size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer);
size_t matchResult = olm_matches_inbound_session_from(sessionPtr, (void const *)theirIdentityKeyPtr, identityKeyLength, (void*)messagePtr , messageLength);
//if(matchResult == olm_error()) {
// for now olm_matches_inbound_session() returns 1 when it succeeds, otherwise 1- or 0
if (matchResult != 1)
{
LOGE("## matchesInboundSessionFromIdKeyJni(): failure - no match Msg=%s",(const char *)olm_session_last_error(sessionPtr));
}
else
{
retCode = JNI_TRUE;
LOGD("## matchesInboundSessionFromIdKeyJni(): success - result=%lu", static_cast(matchResult));
}
}
// free local alloc
if (theirIdentityKeyPtr)
{
env->ReleaseByteArrayElements(aTheirIdentityKeyBuffer, theirIdentityKeyPtr, JNI_ABORT);
}
if (messagePtr)
{
env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT);
}
return retCode;
}
/**
* Encrypt a message using the session.
* An exception is thrown if the operation fails.
* @param aClearMsg clear text message
* @param [out] aEncryptedMsg ciphered message
* @return the encrypted message
*/
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsgBuffer, jobject aEncryptedMsg)
{
jbyteArray encryptedMsgRet = 0;
const char* errorMessage = NULL;
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
jbyte *clearMsgPtr = NULL;
jboolean clearMsgIsCopied = JNI_FALSE;
jclass encryptedMsgJClass = 0;
jfieldID typeMsgFieldId;
LOGD("## encryptMessageJni(): IN ");
if (!sessionPtr)
{
LOGE("## encryptMessageJni(): failure - invalid Session ptr=NULL");
errorMessage = "invalid Session ptr=NULL";
}
else if (!aClearMsgBuffer)
{
LOGE("## encryptMessageJni(): failure - invalid clear message");
errorMessage = "invalid clear message";
}
else if (!aEncryptedMsg)
{
LOGE("## encryptMessageJni(): failure - invalid encrypted message");
errorMessage = "invalid encrypted message";
}
else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, &clearMsgIsCopied)))
{
LOGE("## encryptMessageJni(): failure - clear message JNI allocation OOM");
errorMessage = "clear message JNI allocation OOM";
}
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
{
LOGE("## encryptMessageJni(): failure - unable to get crypted message class");
errorMessage = "unable to get crypted message class";
}
else if (!(typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","J")))
{
LOGE("## encryptMessageJni(): failure - unable to get message type field");
errorMessage = "unable to get message type field";
}
else
{
// get message type
size_t messageType = olm_encrypt_message_type(sessionPtr);
uint8_t *randomBuffPtr = NULL;
// compute random buffer
// Note: olm_encrypt_random_length() can return 0, which means
// it just does not need new random data to encrypt a new message
size_t randomLength = olm_encrypt_random_length(sessionPtr);
LOGD("## encryptMessageJni(): randomLength=%lu", static_cast(randomLength));
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBuffPtr, randomLength))
{
LOGE("## encryptMessageJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
// alloc buffer for encrypted message
size_t clearMsgLength = (size_t)env->GetArrayLength(aClearMsgBuffer);
size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength);
void *encryptedMsgPtr = malloc(encryptedMsgLength*sizeof(uint8_t));
if (!encryptedMsgPtr)
{
LOGE("## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM");
errorMessage = "encryptedMsgPtr buffer OOM";
}
else
{
if (0 == randomLength)
{
LOGW("## encryptMessageJni(): random buffer is not required");
}
LOGD("## encryptMessageJni(): messageType=%lu randomLength=%lu clearMsgLength=%lu encryptedMsgLength=%lu",static_cast(messageType),static_cast(randomLength), static_cast(clearMsgLength), static_cast(encryptedMsgLength));
// encrypt message
size_t result = olm_encrypt(sessionPtr,
(void const *)clearMsgPtr,
clearMsgLength,
randomBuffPtr,
randomLength,
encryptedMsgPtr,
encryptedMsgLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_session_last_error(sessionPtr);
LOGE("## encryptMessageJni(): failure - Msg=%s", errorMessage);
}
else
{
// update message type: PRE KEY or normal
env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType);
encryptedMsgRet = env->NewByteArray(encryptedMsgLength);
env->SetByteArrayRegion(encryptedMsgRet, 0 , encryptedMsgLength, (jbyte*)encryptedMsgPtr);
LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%.*s", static_cast(result), static_cast(messageType), static_cast(result), (const char*)encryptedMsgPtr);
}
free(encryptedMsgPtr);
}
memset(randomBuffPtr, 0, randomLength);
free(randomBuffPtr);
}
}
// free alloc
if (clearMsgPtr)
{
if (clearMsgIsCopied)
{
memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer));
}
env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return encryptedMsgRet;
}
/**
* Decrypt a message using the session.
* An exception is thrown if the operation fails.
* @param aEncryptedMsg message to decrypt
* @return decrypted message if operation succeed
*/
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg)
{
const char* errorMessage = NULL;
jbyteArray decryptedMsgRet = 0;
jclass encryptedMsgJClass = 0;
jstring encryptedMsgJstring = 0; // <= obtained from encryptedMsgFieldId
// field IDs
jfieldID encryptedMsgFieldId;
jfieldID typeMsgFieldId;
// ptrs
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
const char *encryptedMsgPtr = NULL; // <= obtained from encryptedMsgJstring
uint8_t *plainTextMsgPtr = NULL;
char *tempEncryptedPtr = NULL;
LOGD("## decryptMessageJni(): IN - OlmSession");
if (!sessionPtr)
{
LOGE("## decryptMessageJni(): failure - invalid Session ptr=NULL");
errorMessage = "invalid Session ptr=NULL";
}
else if (!aEncryptedMsg)
{
LOGE("## decryptMessageJni(): failure - invalid encrypted message");
errorMessage = "invalid encrypted message";
}
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
{
LOGE("## decryptMessageJni(): failure - unable to get encrypted message class");
errorMessage = "unable to get encrypted message class";
}
else if (!(encryptedMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mCipherText","Ljava/lang/String;")))
{
LOGE("## decryptMessageJni(): failure - unable to get message field");
errorMessage = "unable to get message field";
}
else if (!(typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","J")))
{
LOGE("## decryptMessageJni(): failure - unable to get message type field");
errorMessage = "unable to get message type field";
}
else if (!(encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId)))
{
LOGE("## decryptMessageJni(): failure - JNI encrypted object ");
errorMessage = "JNI encrypted object";
}
else if (!(encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0)))
{
LOGE("## decryptMessageJni(): failure - encrypted message JNI allocation OOM");
errorMessage = "encrypted message JNI allocation OOM";
}
else
{
// get message type
size_t encryptedMsgType = (size_t)env->GetLongField(aEncryptedMsg, typeMsgFieldId);
// get encrypted message length
size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(encryptedMsgJstring);
// create a dedicated temp buffer to be used in next Olm API calls
tempEncryptedPtr = static_cast(malloc(encryptedMsgLength*sizeof(uint8_t)));
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
LOGD("## decryptMessageJni(): MsgType=%lu encryptedMsgLength=%lu encryptedMsg=%.*s",static_cast(encryptedMsgType),static_cast(encryptedMsgLength), static_cast(encryptedMsgLength), encryptedMsgPtr);
// get max plaintext length
size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr,
static_cast(encryptedMsgType),
static_cast(tempEncryptedPtr),
encryptedMsgLength);
// Note: tempEncryptedPtr is destroyed by olm_decrypt_max_plaintext_length()
if (maxPlainTextLength == olm_error())
{
errorMessage = (const char *)olm_session_last_error(sessionPtr);
LOGE("## decryptMessageJni(): failure - olm_decrypt_max_plaintext_length Msg=%s", errorMessage);
}
else
{
LOGD("## decryptMessageJni(): maxPlaintextLength=%lu",static_cast(maxPlainTextLength));
// allocate output decrypted message
plainTextMsgPtr = static_cast(malloc(maxPlainTextLength*sizeof(uint8_t)));
// decrypt, but before reload encrypted buffer (previous one was destroyed)
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
size_t plaintextLength = olm_decrypt(sessionPtr,
encryptedMsgType,
(void*)tempEncryptedPtr,
encryptedMsgLength,
plainTextMsgPtr,
maxPlainTextLength);
if (plaintextLength == olm_error())
{
errorMessage = (const char *)olm_session_last_error(sessionPtr);
LOGE("## decryptMessageJni(): failure - olm_decrypt Msg=%s", errorMessage);
}
else
{
decryptedMsgRet = env->NewByteArray(plaintextLength);
env->SetByteArrayRegion(decryptedMsgRet, 0 , plaintextLength, (jbyte*)plainTextMsgPtr);
LOGD(" ## decryptMessageJni(): UTF-8 Conversion - decrypted returnedLg=%lu OK",static_cast(plaintextLength));
}
memset(plainTextMsgPtr, 0, maxPlainTextLength);
}
}
// free alloc
if (encryptedMsgPtr)
{
env->ReleaseStringUTFChars(encryptedMsgJstring, encryptedMsgPtr);
}
if (tempEncryptedPtr)
{
free(tempEncryptedPtr);
}
if (plainTextMsgPtr)
{
free(plainTextMsgPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return decryptedMsgRet;
}
/**
* Get the session identifier for this session.
* An exception is thrown if the operation fails.
* @return the session identifier
*/
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
LOGD("## getSessionIdentifierJni(): IN ");
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
if (!sessionPtr)
{
LOGE("## getSessionIdentifierJni(): failure - invalid Session ptr=NULL");
errorMessage = "invalid Session ptr=NULL";
}
else
{
// get the size to alloc to contain the id
size_t lengthSessionId = olm_session_id_length(sessionPtr);
LOGD("## getSessionIdentifierJni(): lengthSessionId=%lu",static_cast(lengthSessionId));
void *sessionIdPtr = malloc(lengthSessionId*sizeof(uint8_t));
if (!sessionIdPtr)
{
LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM");
errorMessage = "identifier allocation OOM";
}
else
{
size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
if (result == olm_error())
{
errorMessage = (const char *)olm_session_last_error(sessionPtr);
LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s", errorMessage);
}
else
{
LOGD("## getSessionIdentifierJni(): success - result=%lu sessionId=%.*s",static_cast(result), static_cast(result), (char*)sessionIdPtr);
returnValue = env->NewByteArray(result);
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionIdPtr);
}
free(sessionIdPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Serialize and encrypt session instance.
* An exception is thrown if the operation fails.
* @param aKeyBuffer key used to encrypt the serialized account data
* @return the serialised account as bytes buffer.
**/
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
jbyte* keyPtr = NULL;
OlmSession* sessionPtr = getSessionInstanceId(env, thiz);
LOGD("## serializeJni(): IN");
if (!sessionPtr)
{
LOGE(" ## serializeJni(): failure - invalid session ptr");
errorMessage = "invalid session ptr";
}
else if (!aKeyBuffer)
{
LOGE(" ## serializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, 0)))
{
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "ikeyPtr JNI allocation OOM";
}
else
{
size_t pickledLength = olm_pickle_session_length(sessionPtr);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu",static_cast(pickledLength), static_cast(keyLength));
void *pickledPtr = malloc(pickledLength*sizeof(uint8_t));
if (!pickledPtr)
{
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
errorMessage = "pickledPtr buffer OOM";
}
else
{
size_t result = olm_pickle_session(sessionPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_session_last_error(sessionPtr);
LOGE(" ## serializeJni(): failure - olm_pickle_session() Msg=%s", errorMessage);
}
else
{
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast(result), static_cast(pickledLength), static_cast(pickledPtr));
returnValue = env->NewByteArray(pickledLength);
env->SetByteArrayRegion(returnValue, 0 , pickledLength, (jbyte*)pickledPtr);
}
free(pickledPtr);
}
}
// free alloc
if (keyPtr)
{
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Allocate a new session and initialize it with the serialisation data.
* An exception is thrown if the operation fails.
* @param aSerializedData the session serialisation buffer
* @param aKey the key used to encrypt the serialized account data
* @return the deserialized session
**/
JNIEXPORT jlong OLM_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
OlmSession* sessionPtr = initializeSessionMemory();
jbyte* keyPtr = NULL;
jbyte* pickledPtr = NULL;
LOGD("## deserializeJni(): IN");
if (!sessionPtr)
{
LOGE(" ## deserializeJni(): failure - session failure OOM");
errorMessage = "session failure OOM";
}
else if (!aKeyBuffer)
{
LOGE(" ## deserializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!aSerializedDataBuffer)
{
LOGE(" ## deserializeJni(): failure - serialized data");
errorMessage = "serialized data";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, 0)))
{
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
{
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
errorMessage = "pickledPtr JNI allocation OOM";
}
else
{
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast(pickledLength), static_cast(keyLength));
LOGD(" ## deserializeJni(): pickled=%.*s",static_cast(pickledLength), (char const *)pickledPtr);
size_t result = olm_unpickle_session(sessionPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_session_last_error(sessionPtr);
LOGE(" ## deserializeJni(): failure - olm_unpickle_account() Msg=%s", errorMessage);
}
else
{
LOGD(" ## initJni(): success - result=%lu ", static_cast(result));
}
}
// free alloc
if (keyPtr)
{
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (pickledPtr)
{
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
}
if (errorMessage)
{
if (sessionPtr)
{
olm_clear_session(sessionPtr);
free(sessionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)sessionPtr;
}