aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValere <valeref@matrix.org>2019-03-26 14:30:19 +0100
committerValere <valeref@matrix.org>2019-04-10 12:24:00 +0200
commitc9369a4383714c8656fc0ee72965e46476a56691 (patch)
tree97c3c69d68c43fc6e29590c938db56461216a34a
parentebc156e7c272e08e375c3e704651b179541e078b (diff)
E2E: SAS Verification (olm)
Fix / missing free() on some errors Added doc regarding string encoding for keys cleaning
-rw-r--r--android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSasTest.java99
-rw-r--r--android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java5
-rw-r--r--android/olm-sdk/src/main/java/org/matrix/olm/OlmSAS.java140
-rw-r--r--android/olm-sdk/src/main/jni/Android.mk4
-rw-r--r--android/olm-sdk/src/main/jni/olm_jni.h1
-rw-r--r--android/olm-sdk/src/main/jni/olm_jni_helper.cpp5
-rw-r--r--android/olm-sdk/src/main/jni/olm_jni_helper.h1
-rw-r--r--android/olm-sdk/src/main/jni/olm_sas.cpp310
-rw-r--r--android/olm-sdk/src/main/jni/olm_sas.h40
9 files changed, 604 insertions, 1 deletions
diff --git a/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSasTest.java b/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSasTest.java
new file mode 100644
index 0000000..a757050
--- /dev/null
+++ b/android/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSasTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 New Vector 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.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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class OlmSasTest {
+
+ private static OlmManager mOlmManager;
+
+ //Enable the native lib
+ @BeforeClass
+ public static void setUpClass() {
+ // load native librandomBytesOfLength
+ mOlmManager = new OlmManager();
+ }
+
+ @Test
+ public void testSASCode() {
+ OlmSAS aliceSas = null;
+ OlmSAS bobSas = null;
+
+ try {
+ aliceSas = new OlmSAS();
+ bobSas = new OlmSAS();
+
+ String alicePKey = aliceSas.getPublicKey();
+ String bobPKey = bobSas.getPublicKey();
+
+ Log.e(OlmSasTest.class.getSimpleName(), "#### Alice pub Key is " + alicePKey);
+ Log.e(OlmSasTest.class.getSimpleName(), "#### Bob pub Key is " + bobPKey);
+
+ aliceSas.setTheirPublicKey(bobPKey);
+ bobSas.setTheirPublicKey(alicePKey);
+
+ int codeLength = 6;
+ byte[] alice_sas = aliceSas.generateShortCode("SAS", codeLength);
+ byte[] bob_sas = bobSas.generateShortCode("SAS", codeLength);
+
+ Log.e(OlmSasTest.class.getSimpleName(), "#### Alice SAS is " + new String(alice_sas, "UTF-8"));
+ Log.e(OlmSasTest.class.getSimpleName(), "#### Bob SAS is " + new String(bob_sas, "UTF-8"));
+
+ assertEquals(codeLength, alice_sas.length);
+ assertEquals(codeLength, bob_sas.length);
+ assertArrayEquals(alice_sas, bob_sas);
+
+ byte[] aliceMac = aliceSas.calculateMac("Hello world!", "SAS");
+ byte[] bobMac = bobSas.calculateMac("Hello world!", "SAS");
+
+ assertTrue(aliceMac.length > 0 && bobMac.length > 0);
+ assertEquals(aliceMac.length, bobMac.length);
+ assertArrayEquals(aliceMac, bobMac);
+
+ Log.e(OlmSasTest.class.getSimpleName(), "#### Alice Mac is " + new String(aliceMac, "UTF-8"));
+ Log.e(OlmSasTest.class.getSimpleName(), "#### Bob Mac is " + new String(bobMac, "UTF-8"));
+
+
+ } catch (Exception e) {
+ assertTrue("OlmSas init failed " + e.getMessage(), false);
+ e.printStackTrace();
+ } finally {
+ if (aliceSas != null) {
+ aliceSas.releaseSas();
+ }
+ if (bobSas != null) {
+ bobSas.releaseSas();
+ }
+ }
+ }
+
+}
diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java
index 532f318..5b4a85a 100644
--- a/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java
+++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java
@@ -76,6 +76,11 @@ public class OlmException extends IOException {
public static final int EXCEPTION_CODE_PK_SIGNING_INIT_WITH_SEED = 802;
public static final int EXCEPTION_CODE_PK_SIGNING_SIGN = 803;
+ public static final int EXCEPTION_CODE_SAS_CREATION = 900;
+ public static final int EXCEPTION_CODE_SAS_ERROR = 901;
+ public static final int EXCEPTION_CODE_SAS_MISSING_THEIR_PKEY = 902;
+ public static final int EXCEPTION_CODE_SAS_GENERATE_SHORT_CODE = 903;
+
// exception human readable messages
public static final String EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION = "invalid de-serialized parameters";
diff --git a/android/olm-sdk/src/main/java/org/matrix/olm/OlmSAS.java b/android/olm-sdk/src/main/java/org/matrix/olm/OlmSAS.java
new file mode 100644
index 0000000..2869aa4
--- /dev/null
+++ b/android/olm-sdk/src/main/java/org/matrix/olm/OlmSAS.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 New Vector 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.util.Log;
+
+import java.io.UnsupportedEncodingException;
+
+public class OlmSAS {
+
+ private static final String LOG_TAG = OlmSAS.class.getName();
+ /**
+ * Session Id returned by JNI.
+ * This value uniquely identifies the native SAS instance.
+ **/
+ private transient long mNativeId;
+
+ private String theirPublicKey = null;
+
+ public OlmSAS() throws OlmException {
+ try {
+ mNativeId = createNewSASJni();
+ } catch (Exception e) {
+ throw new OlmException(OlmException.EXCEPTION_CODE_SAS_CREATION, e.getMessage());
+ }
+ }
+
+ /**
+ * Gets the Public Key encoded in Base64 with no padding
+ */
+ public String getPublicKey() throws OlmException {
+ try {
+ byte[] buffer = getPubKeyJni();
+
+ if (null != buffer) {
+ return new String(buffer, "UTF-8");
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
+ throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the public key of other user.
+ *
+ * @param otherPkey other user public key (base64 encoded with no padding)
+ * @throws OlmException
+ */
+ public void setTheirPublicKey(String otherPkey) throws OlmException {
+ try {
+ setTheirPubKey(otherPkey.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
+ }
+ this.theirPublicKey = otherPkey;
+ }
+
+
+ /**
+ * Generate bytes to use for the short authentication string.
+ *
+ * @param info info extra information to mix in when generating the bytes, as
+ * per the Matrix spec.
+ * @param byteNumber The size of the short code to generate
+ * @return The generated shortcode
+ * @throws OlmException
+ */
+ public byte[] generateShortCode(String info, int byteNumber) throws OlmException {
+ if (theirPublicKey == null || theirPublicKey.isEmpty()) {
+ throw new OlmException(OlmException.EXCEPTION_CODE_SAS_MISSING_THEIR_PKEY, "call setTheirPublicKey first");
+ }
+ try {
+ byte[] shortBuffer = generateShortCodeJni(info.getBytes("UTF-8"), byteNumber);
+ return shortBuffer;
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
+ throw new OlmException(OlmException.EXCEPTION_CODE_SAS_GENERATE_SHORT_CODE, e.getMessage());
+ }
+ }
+
+
+ public byte[] calculateMac(String message, String info) throws OlmException {
+ try {
+ return calculateMacJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Create an OLM session in native side.<br>
+ * Do not forget to call {@link #releaseSASJni()} when JAVA side is done.
+ *
+ * @return native account instance identifier or throw an exception.
+ */
+ private native long createNewSASJni();
+
+ /**
+ * Destroy the corresponding OLM session native object.<br>
+ * This method must ALWAYS be called when this JAVA instance
+ * is destroyed (ie. garbage collected) to prevent memory leak in native side.
+ * See {@link #createNewSASJni()}.
+ */
+ private native void releaseSASJni();
+
+ private native byte[] getPubKeyJni();
+
+ private native void setTheirPubKey(byte[] pubKey);
+
+ private native byte[] generateShortCodeJni(byte[] info, int byteNumber);
+
+ private native byte[] calculateMacJni(byte[] message, byte[] info);
+
+ /**
+ * Release native session and invalid its JAVA reference counter part.<br>
+ * Public API for {@link #releaseSASJni()}.
+ */
+ public void releaseSas() {
+ if (0 != mNativeId) {
+ releaseSASJni();
+ }
+ mNativeId = 0;
+ }
+}
diff --git a/android/olm-sdk/src/main/jni/Android.mk b/android/olm-sdk/src/main/jni/Android.mk
index 0d98f69..101346b 100644
--- a/android/olm-sdk/src/main/jni/Android.mk
+++ b/android/olm-sdk/src/main/jni/Android.mk
@@ -41,6 +41,7 @@ $(SRC_ROOT_DIR)/src/ratchet.cpp \
$(SRC_ROOT_DIR)/src/session.cpp \
$(SRC_ROOT_DIR)/src/utility.cpp \
$(SRC_ROOT_DIR)/src/pk.cpp \
+$(SRC_ROOT_DIR)/src/sas.c \
$(SRC_ROOT_DIR)/src/ed25519.c \
$(SRC_ROOT_DIR)/src/error.c \
$(SRC_ROOT_DIR)/src/inbound_group_session.c \
@@ -57,7 +58,8 @@ olm_inbound_group_session.cpp \
olm_outbound_group_session.cpp \
olm_utility.cpp \
olm_manager.cpp \
-olm_pk.cpp
+olm_pk.cpp \
+olm_sas.cpp
LOCAL_LDLIBS := -llog
diff --git a/android/olm-sdk/src/main/jni/olm_jni.h b/android/olm-sdk/src/main/jni/olm_jni.h
index 0a50c5f..110f089 100644
--- a/android/olm-sdk/src/main/jni/olm_jni.h
+++ b/android/olm-sdk/src/main/jni/olm_jni.h
@@ -73,6 +73,7 @@ struct OlmUtility* getUtilityInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmPkDecryption* getPkDecryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmPkEncryption* getPkEncryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmPkSigning* getPkSigningInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
+struct OlmSAS* getOlmSasInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
#ifdef __cplusplus
}
diff --git a/android/olm-sdk/src/main/jni/olm_jni_helper.cpp b/android/olm-sdk/src/main/jni/olm_jni_helper.cpp
index f13c5e1..47f83a8 100644
--- a/android/olm-sdk/src/main/jni/olm_jni_helper.cpp
+++ b/android/olm-sdk/src/main/jni/olm_jni_helper.cpp
@@ -227,3 +227,8 @@ struct OlmPkSigning* getPkSigningInstanceId(JNIEnv* aJniEnv, jobject aJavaObject
{
return (struct OlmPkSigning*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_SIGNING);
}
+
+struct OlmSAS* getOlmSasInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
+{
+ return (struct OlmSAS*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_SAS);
+}
diff --git a/android/olm-sdk/src/main/jni/olm_jni_helper.h b/android/olm-sdk/src/main/jni/olm_jni_helper.h
index e9c03c8..22552b4 100644
--- a/android/olm-sdk/src/main/jni/olm_jni_helper.h
+++ b/android/olm-sdk/src/main/jni/olm_jni_helper.h
@@ -28,4 +28,5 @@ namespace AndroidOlmSdk
static const char *CLASS_OLM_PK_ENCRYPTION = "org/matrix/olm/OlmPkEncryption";
static const char *CLASS_OLM_PK_DECRYPTION = "org/matrix/olm/OlmPkDecryption";
static const char *CLASS_OLM_PK_SIGNING = "org/matrix/olm/OlmPkSigning";
+ static const char *CLASS_OLM_SAS = "org/matrix/olm/OlmSAS";
}
diff --git a/android/olm-sdk/src/main/jni/olm_sas.cpp b/android/olm-sdk/src/main/jni/olm_sas.cpp
new file mode 100644
index 0000000..2ad1e0f
--- /dev/null
+++ b/android/olm-sdk/src/main/jni/olm_sas.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2019 New Vector 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_sas.h"
+
+#include "olm/olm.h"
+
+using namespace AndroidOlmSdk;
+
+JNIEXPORT jlong OLM_SAS_FUNC_DEF(createNewSASJni)(JNIEnv *env, jobject thiz)
+{
+
+ size_t sasSize = olm_sas_size();
+ OlmSAS *sasPtr = (OlmSAS *) malloc(sasSize);
+ const char* errorMessage = NULL;
+
+ if (!sasPtr)
+ {
+ LOGE("## createNewSASJni(): failure - init SAS OOM");
+ env->ThrowNew(env->FindClass("java/lang/Exception"), "init sas OOM");
+ }
+ else
+ {
+ sasPtr = olm_sas(sasPtr)
+ LOGD(" ## createNewSASJni(): success - sasPtr=%p (jlong)(intptr_t)accountPtr=%lld",sasPtr,(jlong)(intptr_t)sasPtr);
+ }
+
+ size_t randomSize = olm_create_sas_random_length(sasPtr);
+ uint8_t *randomBuffPtr = NULL;
+
+ LOGD("## createNewSASJni(): randomSize=%lu",static_cast<long unsigned int>(randomSize));
+
+ if ( (0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize))
+ {
+ LOGE("## createNewSASJni(): failure - random buffer init");
+ errorMessage = "Failed to init private key";
+ }
+ else
+ {
+ size_t result = olm_create_sas(sasPtr, randomBuffPtr, randomSize);
+ if (result == olm_error())
+ {
+ errorMessage = (const char *)olm_sas_last_error(sasPtr);
+ LOGE("## createNewSASJni(): failure - error creating SAS Msg=%s", errorMessage);
+ }
+ }
+
+ if (randomBuffPtr)
+ {
+ memset(randomBuffPtr, 0, randomSize);
+ free(randomBuffPtr);
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return (jlong)(intptr_t)sasPtr;
+}
+
+JNIEXPORT void OLM_SAS_FUNC_DEF(releaseSASJni)(JNIEnv *env, jobject thiz)
+{
+ LOGD("## releaseSASJni(): IN");
+ OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
+
+ if (!sasPtr)
+ {
+ LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
+ }
+ else
+ {
+ olm_clear_sas(sasPtr);
+ // even if free(NULL) does not crash, logs are performed for debug purpose
+ free(sasPtr);
+ }
+}
+
+
+JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(getPubKeyJni)(JNIEnv *env, jobject thiz)
+{
+ LOGD("## getPubKeyJni(): IN");
+ const char* errorMessage = NULL;
+ jbyteArray returnValue = 0;
+ OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
+
+ if (!sasPtr)
+ {
+ LOGE("## getPubKeyJni(): failure - invalid SAS ptr=NULL");
+ errorMessage = "invalid SAS ptr=NULL";
+ }
+ else
+ {
+ size_t pubKeyLength = olm_sas_pubkey_length(sasPtr);
+ void *pubkey = malloc(pubKeyLength*sizeof(uint8_t));
+ size_t result = olm_sas_get_pubkey(sasPtr, pubkey, pubKeyLength);
+ if (result == olm_error())
+ {
+ errorMessage = (const char *)olm_sas_last_error(sasPtr);
+ LOGE("## getPubKeyJni(): failure - error getting pub key Msg=%s", errorMessage);
+ }
+ else
+ {
+ returnValue = env->NewByteArray(pubKeyLength);
+ env->SetByteArrayRegion(returnValue, 0 , pubKeyLength, (jbyte*)pubkey);
+ }
+ if (pubkey) {
+ free(pubkey);
+ }
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return returnValue;
+}
+
+JNIEXPORT void OLM_SAS_FUNC_DEF(setTheirPubKey)(JNIEnv *env, jobject thiz,jbyteArray pubKeyBuffer) {
+
+ OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
+
+ const char* errorMessage = NULL;
+ jbyte *pubKeyPtr = NULL;
+ jboolean pubKeyWasCopied = JNI_FALSE;
+
+ if (!sasPtr)
+ {
+ LOGE("## setTheirPubKey(): failure - invalid SAS ptr=NULL");
+ errorMessage = "invalid SAS ptr=NULL";
+ } else if(!pubKeyBuffer) {
+ LOGE("## setTheirPubKey(): failure - invalid info");
+ errorMessage = "invalid pubKey";
+ }
+ else if (!(pubKeyPtr = env->GetByteArrayElements(pubKeyBuffer, &pubKeyWasCopied)))
+ {
+ LOGE(" ## generateShortCodeJni(): failure - info JNI allocation OOM");
+ errorMessage = "info JNI allocation OOM";
+ }
+ else
+ {
+ size_t pubKeyLength = (size_t)env->GetArrayLength(pubKeyBuffer);
+ size_t result = olm_sas_set_their_key(sasPtr,pubKeyPtr,pubKeyLength);
+ if (result == olm_error())
+ {
+ errorMessage = (const char *)olm_sas_last_error(sasPtr);
+ LOGE("## setTheirPubKey(): failure - error setting their key Msg=%s", errorMessage);
+ }
+ }
+ // free alloc
+ if (pubKeyPtr)
+ {
+ if (pubKeyWasCopied)
+ {
+ memset(pubKeyPtr, 0, (size_t)env->GetArrayLength(pubKeyBuffer));
+ }
+ env->ReleaseByteArrayElements(pubKeyBuffer, pubKeyPtr, JNI_ABORT);
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+}
+
+JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(generateShortCodeJni)(JNIEnv *env, jobject thiz, jbyteArray infoStringBytes, jint byteNb) {
+ LOGD("## generateShortCodeJni(): IN");
+ const char* errorMessage = NULL;
+ jbyteArray returnValue = 0;
+ OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
+
+ jbyte *infoPtr = NULL;
+ jboolean infoWasCopied = JNI_FALSE;
+
+ if (!sasPtr)
+ {
+ LOGE("## generateShortCodeJni(): failure - invalid SAS ptr=NULL");
+ errorMessage = "invalid SAS ptr=NULL";
+ } else if(!infoStringBytes) {
+ LOGE("## generateShortCodeJni(): failure - invalid info");
+ errorMessage = "invalid info";
+ }
+ else if (!(infoPtr = env->GetByteArrayElements(infoStringBytes, &infoWasCopied)))
+ {
+ LOGE(" ## generateShortCodeJni(): failure - info JNI allocation OOM");
+ errorMessage = "info JNI allocation OOM";
+ }
+ else {
+ size_t shortBytesCodeLength = (size_t) byteNb;
+ void *shortBytesCode = malloc(shortBytesCodeLength * sizeof(uint8_t));
+ size_t infoLength = (size_t)env->GetArrayLength(infoStringBytes);
+ olm_sas_generate_bytes(sasPtr, infoPtr, infoLength, shortBytesCode, shortBytesCodeLength);
+ returnValue = env->NewByteArray(shortBytesCodeLength);
+ env->SetByteArrayRegion(returnValue, 0 , shortBytesCodeLength, (jbyte*)shortBytesCode);
+ free(shortBytesCode);
+ }
+
+ // free alloc
+ if (infoPtr)
+ {
+ if (infoWasCopied)
+ {
+ memset(infoPtr, 0, (size_t)env->GetArrayLength(infoStringBytes));
+ }
+ env->ReleaseByteArrayElements(infoStringBytes, infoPtr, JNI_ABORT);
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return returnValue;
+}
+
+
+JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
+ LOGD("## calculateMacJni(): IN");
+ const char* errorMessage = NULL;
+ jbyteArray returnValue = 0;
+ OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
+
+ jbyte *messagePtr = NULL;
+ jboolean messageWasCopied = JNI_FALSE;
+
+ jbyte *infoPtr = NULL;
+ jboolean infoWasCopied = JNI_FALSE;
+
+ if (!sasPtr)
+ {
+ LOGE("## calculateMacJni(): failure - invalid SAS ptr=NULL");
+ errorMessage = "invalid SAS ptr=NULL";
+ } else if(!messageBuffer) {
+ LOGE("## calculateMacJni(): failure - invalid message");
+ errorMessage = "invalid info";
+ }
+ else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
+ {
+ LOGE(" ## calculateMacJni(): failure - message JNI allocation OOM");
+ errorMessage = "message JNI allocation OOM";
+ }
+ else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
+ {
+ LOGE(" ## calculateMacJni(): failure - info JNI allocation OOM");
+ errorMessage = "info JNI allocation OOM";
+ } else {
+
+ size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
+ size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
+ size_t macLength = olm_sas_mac_length(sasPtr);
+
+ void *macPtr = malloc(macLength*sizeof(uint8_t));
+
+ size_t result = olm_sas_calculate_mac(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
+ if (result == olm_error())
+ {
+ errorMessage = (const char *)olm_sas_last_error(sasPtr);
+ LOGE("## calculateMacJni(): failure - error calculating SAS mac Msg=%s", errorMessage);
+ }
+ else
+ {
+ returnValue = env->NewByteArray(macLength);
+ env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
+ }
+
+ if (macPtr) {
+ free(macPtr);
+ }
+ }
+
+ // free alloc
+ if (infoPtr)
+ {
+ if (infoWasCopied)
+ {
+ memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
+ }
+ env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
+ }
+ if (messagePtr)
+ {
+ if (messageWasCopied)
+ {
+ memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
+ }
+ env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
+ }
+
+ if (errorMessage)
+ {
+ env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
+ }
+
+ return returnValue;
+} \ No newline at end of file
diff --git a/android/olm-sdk/src/main/jni/olm_sas.h b/android/olm-sdk/src/main/jni/olm_sas.h
new file mode 100644
index 0000000..ffd4494
--- /dev/null
+++ b/android/olm-sdk/src/main/jni/olm_sas.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 New Vector 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.
+ */
+
+#ifndef _OMLSAS_H
+#define _OMLSAS_H
+
+#include "olm_jni.h"
+#include "olm/sas.h"
+
+#define OLM_SAS_FUNC_DEF(func_name) FUNC_DEF(OlmSAS,func_name)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jlong OLM_SAS_FUNC_DEF(createNewSASJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT void OLM_SAS_FUNC_DEF(releaseSASJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(getPubKeyJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT void OLM_SAS_FUNC_DEF(setTheirPubKey)(JNIEnv *env, jobject thiz,jbyteArray pubKey);
+JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(generateShortCodeJni)(JNIEnv *env, jobject thiz, jbyteArray infoStringBytes, jint byteNb);
+JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif