diff --git a/libsession-util/libsession-util b/libsession-util/libsession-util index b80d9d9afa..fa7e0d0acf 160000 --- a/libsession-util/libsession-util +++ b/libsession-util/libsession-util @@ -1 +1 @@ -Subproject commit b80d9d9afa725da528d23000062bc34551e4326c +Subproject commit fa7e0d0acf9c343f0d7f0a23e5852077c2cf288f diff --git a/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt b/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt index e9d78475ad..294efd1574 100644 --- a/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt +++ b/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt @@ -3,6 +3,8 @@ package network.loki.messenger.libsession_util import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import network.loki.messenger.libsession_util.util.Sodium +import network.loki.messenger.libsession_util.util.UserPic import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith @@ -15,6 +17,10 @@ import org.session.libsignal.utilities.Hex */ @RunWith(AndroidJUnit4::class) class InstrumentedTests { + + val seed = Hex.fromStringCondensed("0123456789abcdef0123456789abcdef00000000000000000000000000000000") + val keyPair = Sodium.ed25519KeyPair(seed) + @Test fun useAppContext() { // Context of the app under test. @@ -24,15 +30,14 @@ class InstrumentedTests { @Test fun jni_accessible() { - val userProfile = UserProfile.newInstance() + val userProfile = UserProfile.newInstance(keyPair.secretKey) assertNotNull(userProfile) userProfile.free() } @Test fun jni_user_profile_c_api() { - Log.d("JNITEST","Creating profile") - val userProfile = UserProfile.newInstance() + val userProfile = UserProfile.newInstance(keyPair.secretKey) // these should be false as empty config assertFalse(userProfile.needsPush()) @@ -53,9 +58,7 @@ class InstrumentedTests { // not sending keylen like c api so cutting off the NOTSECRET in key for testing purposes userProfile.setName("Kallie") val newUserPic = UserPic("http://example.org/omg-pic-123.bmp", "secret".encodeToByteArray()) - Log.d("JNITEST","setting pic") userProfile.setPic(newUserPic) - Log.d("JNITEST","set pic") // Retrieve them just to make sure they set properly: assertEquals("Kallie", userProfile.getName()) @@ -110,7 +113,7 @@ class InstrumentedTests { @Test fun jni_setting_getting() { - val userProfile = UserProfile.newInstance() + val userProfile = UserProfile.newInstance(keyPair.secretKey) val newName = "test" println("Name being set via JNI call: $newName") userProfile.setName(newName) diff --git a/libsession-util/src/main/cpp/session_util.cpp b/libsession-util/src/main/cpp/session_util.cpp index 2fbc41d005..94d5a70935 100644 --- a/libsession-util/src/main/cpp/session_util.cpp +++ b/libsession-util/src/main/cpp/session_util.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "session/config/user_profile.hpp" session::config::ConfigBase* ptrToConfigBase(JNIEnv *env, jobject obj) { @@ -14,7 +15,7 @@ session::config::UserProfile* ptrToProfile(JNIEnv* env, jobject obj) { return (session::config::UserProfile*) env->GetLongField(obj, pointerField); } -jbyteArray bytes_from_string(JNIEnv* env, std::string_view from_str) { +jbyteArray bytes_from_ustring(JNIEnv* env, session::ustring_view from_str) { size_t length = from_str.length(); jsize jlength = (jsize)length; jbyteArray new_array = env->NewByteArray(jlength); @@ -22,20 +23,36 @@ jbyteArray bytes_from_string(JNIEnv* env, std::string_view from_str) { return new_array; } -std::string string_from_bytes(JNIEnv* env, jbyteArray byteArray) { +session::ustring ustring_from_bytes(JNIEnv* env, jbyteArray byteArray) { size_t len = env->GetArrayLength(byteArray); jbyte* bytes = env->GetByteArrayElements(byteArray, nullptr); - std::string newSt((char*)bytes, len); + session::ustring newSt((u_char*)bytes, len); env->ReleaseByteArrayElements(byteArray, bytes, 0); return newSt; } -extern "C" JNIEXPORT jobject JNICALL -Java_network_loki_messenger_libsession_1util_UserProfile_00024Companion_newInstance( - JNIEnv* env, - jobject) { +extern "C" +JNIEXPORT jobject JNICALL +Java_network_loki_messenger_libsession_1util_UserProfile_00024Companion_newInstance___3B_3B( + JNIEnv *env, jobject thiz, jbyteArray ed25519_secret_key, jbyteArray initial_dump) { + auto secret_key = ustring_from_bytes(env, ed25519_secret_key); + auto initial = ustring_from_bytes(env, initial_dump); + auto* profile = new session::config::UserProfile(secret_key, std::optional(initial)); - auto* profile = new session::config::UserProfile(); + jclass userClass = env->FindClass("network/loki/messenger/libsession_util/UserProfile"); + jmethodID constructor = env->GetMethodID(userClass, "", "(J)V"); + jobject newConfig = env->NewObject(userClass, constructor, reinterpret_cast(profile)); + + return newConfig; +} + +extern "C" JNIEXPORT jobject JNICALL +Java_network_loki_messenger_libsession_1util_UserProfile_00024Companion_newInstance___3B( + JNIEnv* env, + jobject, + jbyteArray secretKey) { + + auto* profile = new session::config::UserProfile(ustring_from_bytes(env, secretKey), std::nullopt); jclass userClass = env->FindClass("network/loki/messenger/libsession_util/UserProfile"); jmethodID constructor = env->GetMethodID(userClass, "", "(J)V"); @@ -58,8 +75,8 @@ JNIEXPORT jstring JNICALL Java_network_loki_messenger_libsession_1util_UserProfile_getName(JNIEnv *env, jobject thiz) { auto profile = ptrToProfile(env, thiz); auto name = profile->get_name(); - if (name == nullptr) return nullptr; - jstring returnString = env->NewStringUTF(name->c_str()); + if (name == std::nullopt) return nullptr; + jstring returnString = env->NewStringUTF(name->data()); return returnString; } @@ -96,8 +113,8 @@ JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_push(JNIEnv *env, jobject thiz) { auto config = ptrToConfigBase(env, thiz); auto pair = config->push(); - std::string to_push_str = pair.first; - jbyteArray returnByteArray = bytes_from_string(env, to_push_str); + session::ustring to_push_str = pair.first; + jbyteArray returnByteArray = bytes_from_ustring(env, to_push_str); jlong seqNo = pair.second; jclass returnObjectClass = env->FindClass("network/loki/messenger/libsession_util/util/ConfigWithSeqNo"); jmethodID methodId = env->GetMethodID(returnObjectClass, "", "([BJ)V"); @@ -109,15 +126,13 @@ extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_UserProfile_getPic(JNIEnv *env, jobject thiz) { auto profile = ptrToProfile(env, thiz); - auto pair = profile->get_profile_pic(); + auto pic = profile->get_profile_pic(); + if (pic == std::nullopt) return nullptr; // return nullptr if either parameter is null as per profile class - if (pair.first == nullptr || pair.second == nullptr) return nullptr; - const std::string* pic_url = pair.first; - const std::string* pic_key = pair.second; jclass returnObjectClass = env->FindClass("network/loki/messenger/libsession_util/UserPic"); jmethodID constructor = env->GetMethodID(returnObjectClass, "", "(Ljava/lang/String;[B)V"); - jstring url = env->NewStringUTF(pic_url->c_str()); - jbyteArray byteArray = bytes_from_string(env, *pic_key); + jstring url = env->NewStringUTF(pic->url.data()); + jbyteArray byteArray = bytes_from_ustring(env, pic->key); jobject returnObject = env->NewObject(returnObjectClass, constructor, url, byteArray); return returnObject; } @@ -133,7 +148,7 @@ Java_network_loki_messenger_libsession_1util_UserProfile_setPic(JNIEnv *env, job auto key = (jbyteArray)env->GetObjectField(user_pic, keyField); const char* pic_chars = env->GetStringUTFChars(pic, nullptr); - const std::string key_str = string_from_bytes(env, key); + auto key_str = ustring_from_bytes(env, key); auto* pic_string = new std::string(pic_chars); profile->set_profile_pic(*pic_string, key_str); @@ -142,7 +157,24 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_dump(JNIEnv *env, jobject thiz) { auto config = ptrToConfigBase(env, thiz); - std::string dumped = config->dump(); - jbyteArray bytes = bytes_from_string(env, dumped); + auto dumped = config->dump(); + jbyteArray bytes = bytes_from_ustring(env, dumped); return bytes; +} +extern "C" +JNIEXPORT jobject JNICALL +Java_network_loki_messenger_libsession_1util_util_Sodium_ed25519KeyPair(JNIEnv *env, jobject thiz, jbyteArray seed) { + std::array ed_pk; + std::array ed_sk; + auto seed_bytes = ustring_from_bytes(env, seed); + crypto_sign_ed25519_seed_keypair(ed_pk.data(), ed_sk.data(), seed_bytes.c_str()); + + jclass kp_class = env->FindClass("network/loki/messenger/libsession_util/util/KeyPair"); + jmethodID kp_constructor = env->GetMethodID(kp_class, "", "([B[B)V"); + + jbyteArray pk_jarray = bytes_from_ustring(env, ed_pk.data()); + jbyteArray sk_jarray = bytes_from_ustring(env, ed_sk.data()); + + jobject return_obj = env->NewObject(kp_class, kp_constructor, pk_jarray, sk_jarray); + return return_obj; } \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index 7464cd26f6..347c221130 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -1,6 +1,7 @@ package network.loki.messenger.libsession_util import network.loki.messenger.libsession_util.util.ConfigWithSeqNo +import network.loki.messenger.libsession_util.util.UserPic sealed class ConfigBase(protected val /* yucky */ pointer: Long) { @@ -21,31 +22,12 @@ class UserProfile(pointer: Long): ConfigBase(pointer) { init { System.loadLibrary("session_util") } - external fun newInstance(): UserProfile + external fun newInstance(ed25519SecretKey: ByteArray): UserProfile + external fun newInstance(ed25519SecretKey: ByteArray, initialDump: ByteArray): UserProfile } external fun setName(newName: String) external fun getName(): String? external fun free() external fun getPic(): UserPic? external fun setPic(userPic: UserPic) -} - -data class UserPic(val url: String, val key: ByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as UserPic - - if (url != other.url) return false - if (!key.contentEquals(other.key)) return false - - return true - } - - override fun hashCode(): Int { - var result = url.hashCode() - result = 31 * result + key.contentHashCode() - return result - } } \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Sodium.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Sodium.kt new file mode 100644 index 0000000000..c5df22a9ce --- /dev/null +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Sodium.kt @@ -0,0 +1,5 @@ +package network.loki.messenger.libsession_util.util + +object Sodium { + external fun ed25519KeyPair(seed: ByteArray): KeyPair +} \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt index 8a6b96e915..79fc00b70c 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt @@ -19,4 +19,44 @@ data class ConfigWithSeqNo(val config: ByteArray, val seqNo: Long) { return result } +} + +data class UserPic(val url: String, val key: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UserPic + + if (url != other.url) return false + if (!key.contentEquals(other.key)) return false + + return true + } + + override fun hashCode(): Int { + var result = url.hashCode() + result = 31 * result + key.contentHashCode() + return result + } +} + +data class KeyPair(val pubKey: ByteArray, val secretKey: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as KeyPair + + if (!pubKey.contentEquals(other.pubKey)) return false + if (!secretKey.contentEquals(other.secretKey)) return false + + return true + } + + override fun hashCode(): Int { + var result = pubKey.contentHashCode() + result = 31 * result + secretKey.contentHashCode() + return result + } } \ No newline at end of file