Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
val kotlinVersion = "2.2.21"
val kotlinVersion = "2.3.20"

id("com.android.library") version "8.13.1" apply false
id("org.jetbrains.kotlin.android") version kotlinVersion apply false
id("com.android.library") version "9.1.0" apply false
id("org.jetbrains.kotlin.plugin.serialization") version kotlinVersion apply false
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
3 changes: 1 addition & 2 deletions library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.serialization")
id("maven-publish")
id("com.google.protobuf") version "0.9.5"
id("com.google.protobuf") version "0.10.0"
}

group = "org.sessionfoundation"
Expand Down
14 changes: 14 additions & 0 deletions library/src/main/cpp/ed25519.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "util.h"

#include <session/ed25519.hpp>
#include <session/session_protocol.hpp>
#include <session/xed25519.hpp>

extern "C"
Expand Down Expand Up @@ -60,6 +61,19 @@ Java_network_loki_messenger_libsession_1util_ED25519_generateProMasterKey(JNIEnv
});
}

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_network_loki_messenger_libsession_1util_ED25519_proRotatingSeed(JNIEnv *env, jobject thiz,
jbyteArray master_seed,
jlong unix_ts) {
return jni_utils::run_catching_cxx_exception_or_throws<jbyteArray>(env, [=] {
auto seed = session::ProProof::rotating_seed(
jni_utils::JavaByteArrayRef(env, master_seed).get(),
std::chrono::sys_seconds{std::chrono::seconds{unix_ts}});
return util::bytes_from_span(env, seed).release();
});
}

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_network_loki_messenger_libsession_1util_ED25519_positiveEd25519PubKeyFromCurve25519(
Expand Down
33 changes: 2 additions & 31 deletions library/src/main/cpp/jni_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <jni.h>
#include <exception>
#include <ranges>
#include <string>
#include <string_view>
#include <type_traits>

namespace jni_utils {
Expand Down Expand Up @@ -246,37 +248,6 @@ namespace jni_utils {
}
};

class JavaCharsRef {
JNIEnv *env;
jstring s;
std::span<jchar> data;

public:
JavaCharsRef(JNIEnv *env, jstring s) : env(env), s(s) {
const jchar *c_str = env->GetStringChars(s, nullptr);
data = std::span<jchar>(const_cast<jchar *>(c_str), env->GetStringLength(s));
}

JavaCharsRef(const JavaCharsRef &) = delete;

~JavaCharsRef() {
env->ReleaseStringChars(s, data.data());
}

const jchar* chars() const {
return data.data();
}

size_t size() const {
return data.size();
}

// Get the data as a span. Only valid during the lifetime of this object.
std::span<jchar> get() const {
return data;
}
};

/**
* A RAII wrapper for a Java byte array. This will automatically release the byte array when it goes out of scope.
*/
Expand Down
85 changes: 12 additions & 73 deletions library/src/main/cpp/pro_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,18 @@ std::string_view plan_unit_to_string(pb::ProPlanUnit unit) {

JavaLocalRef<jobject> serialize_payment_item(JNIEnv* env, const pb::ProPaymentItem& it) {
static BasicJavaClassInfo cls(env, "network/loki/messenger/libsession_util/pro/ProPaymentItem",
"(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;ZJJJJJJJLjava/lang/String;)V");
"(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;ZJJJJJLjava/lang/String;)V");
return {env, env->NewObject(cls.java_class, cls.constructor,
jstring_from_optional(env, std::string_view(it.status)).get(), // opaque status code string
static_cast<jint>(it.plan.count),
jstring_from_optional(env, plan_unit_to_string(it.plan.unit)).get(),
jstring_from_optional(env, std::string_view(it.payment_provider)).get(),
static_cast<jboolean>(it.auto_renewing),
static_cast<jlong>(it.purchased_at.time_since_epoch().count()), // ms
static_cast<jlong>(it.redeemed_at.time_since_epoch().count()), // s
static_cast<jlong>(it.expiry_at.time_since_epoch().count()), // s
static_cast<jlong>(it.grace_period_duration.count()), // s
static_cast<jlong>(it.platform_refund_expiry_at.time_since_epoch().count()), // s
static_cast<jlong>(it.revoked_at.time_since_epoch().count()), // ms
static_cast<jlong>(it.refund_requested_at.time_since_epoch().count()), // s
jstring_from_optional(env, std::string_view(it.payment_id)).get())};
}

Expand All @@ -103,23 +101,29 @@ JavaLocalRef<jobject> serialize_revocation_item(JNIEnv* env, const pb::ProRevoca
static_cast<jlong>(it.effective_at.time_since_epoch().count()))};
}

jobject serialize_pro_proof_response(JNIEnv* env, const pb::ProProofResponse& resp) {
jobject serialize_pro_proof_response(JNIEnv* env, const pb::GenerateProProofResponse& resp) {
static BasicJavaClassInfo cls(env, "network/loki/messenger/libsession_util/pro/ProProofResponse",
"(Lnetwork/loki/messenger/libsession_util/pro/ProResponseHeader;"
"Lnetwork/loki/messenger/libsession_util/pro/ProProof;)V");
"Lnetwork/loki/messenger/libsession_util/pro/ProProof;J)V");
auto header = serialize_response_header(env, resp);
JavaLocalRef<jobject> proof(env, nullptr);
if (resp) // ResponseBase::operator bool: true iff status == Ok (proof populated on success)
proof = cpp_to_java_proof(env, resp.proof);
return env->NewObject(cls.java_class, cls.constructor, header.get(), proof.get());
// Advisory account expiry as epoch seconds (0 = absent); present on success + subscription_expired.
jlong account_expiry_s =
resp.account_expiry
? static_cast<jlong>(resp.account_expiry->time_since_epoch().count())
: 0;
return env->NewObject(
cls.java_class, cls.constructor, header.get(), proof.get(), account_expiry_s);
}

jobject serialize_pro_status_response(JNIEnv* env, const pb::ProStatusResponse& resp) {
// §3.4: get_pro_status carries a single optional
// latest_payment (has-flag + nullable item) instead of an items[] list + payments_total.
static BasicJavaClassInfo cls(env, "network/loki/messenger/libsession_util/pro/GetProStatusResponse",
"(Lnetwork/loki/messenger/libsession_util/pro/ProResponseHeader;Ljava/lang/String;Z"
"Lnetwork/loki/messenger/libsession_util/pro/ProPaymentItem;IZJJJ)V");
"Lnetwork/loki/messenger/libsession_util/pro/ProPaymentItem;ZJJ)V");
auto header = serialize_response_header(env, resp);
JavaLocalRef<jobject> latest_payment(env, nullptr);
if (resp.latest_payment)
Expand All @@ -129,11 +133,9 @@ jobject serialize_pro_status_response(JNIEnv* env, const pb::ProStatusResponse&
jstring_from_optional(env, std::string_view(resp.user_status)).get(), // opaque status code string
static_cast<jboolean>(resp.latest_payment.has_value()),
latest_payment.get(),
static_cast<jint>(resp.error_report),
static_cast<jboolean>(resp.auto_renewing),
static_cast<jlong>(resp.expiry_at.time_since_epoch().count()),
static_cast<jlong>(resp.grace_period_duration.count()),
static_cast<jlong>(resp.refund_requested_at.time_since_epoch().count()));
static_cast<jlong>(resp.grace_period_duration.count()));
}

jobject serialize_revocations_response(JNIEnv* env, const pb::GetProRevocationsResponse& resp) {
Expand All @@ -152,35 +154,10 @@ jobject serialize_revocations_response(JNIEnv* env, const pb::GetProRevocationsR
items.get());
}

jobject serialize_refund_response(JNIEnv* env, const pb::SetPaymentRefundRequestedResponse& resp) {
static BasicJavaClassInfo cls(env, "network/loki/messenger/libsession_util/pro/SetPaymentRefundRequestedResponse",
"(Lnetwork/loki/messenger/libsession_util/pro/ProResponseHeader;Z)V");
auto header = serialize_response_header(env, resp);
return env->NewObject(cls.java_class, cls.constructor, header.get(),
static_cast<jboolean>(resp.updated));
}

} // namespace

// --- Request builders (return ProRequest{endpoint, body}) ---

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_buildAddProPaymentRequest(
JNIEnv* env, jobject, jbyteArray master_private_key, jbyteArray rotating_private_key,
jstring provider_code, jstring payment_id) {
return run_catching_cxx_exception_or_throws<jobject>(env, [=]() {
JavaStringRef provider_ref(env, provider_code);
JavaStringRef payment_ref(env, payment_id);
auto req = pb::add_payment_request(
JavaByteArrayRef(env, master_private_key).get(),
JavaByteArrayRef(env, rotating_private_key).get(),
provider_ref.view(),
string_to_span(payment_ref.view()));
return serialize_pro_request(env, req);
});
}

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_buildGenerateProProofRequest(
Expand All @@ -207,24 +184,6 @@ Java_network_loki_messenger_libsession_1util_pro_BackendRequests_buildGetProStat
});
}

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_buildRefundRequest(
JNIEnv* env, jobject, jbyteArray master_private_key, jlong now_seconds,
jlong refund_requested_seconds, jstring provider_code, jstring payment_id) {
return run_catching_cxx_exception_or_throws<jobject>(env, [=]() {
JavaStringRef provider_ref(env, provider_code);
JavaStringRef payment_ref(env, payment_id);
auto req = pb::refund_request(
JavaByteArrayRef(env, master_private_key).get(),
std::chrono::sys_seconds{std::chrono::seconds(now_seconds)},
std::chrono::sys_seconds{std::chrono::seconds(refund_requested_seconds)},
provider_ref.view(),
string_to_span(payment_ref.view()));
return serialize_pro_request(env, req);
});
}

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_buildRevocationsRequest(
Expand All @@ -237,16 +196,6 @@ Java_network_loki_messenger_libsession_1util_pro_BackendRequests_buildRevocation

// --- Response parsers (return typed structs) ---

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_parseAddPaymentResponse(
JNIEnv* env, jobject, jstring json) {
return run_catching_cxx_exception_or_throws<jobject>(env, [=]() {
JavaStringRef json_ref(env, json);
return serialize_pro_proof_response(env, pb::parse_add_payment(json_ref.view()));
});
}

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_parseProProofResponse(
Expand All @@ -267,16 +216,6 @@ Java_network_loki_messenger_libsession_1util_pro_BackendRequests_parseProStatusR
});
}

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_parseRefundResponse(
JNIEnv* env, jobject, jstring json) {
return run_catching_cxx_exception_or_throws<jobject>(env, [=]() {
JavaStringRef json_ref(env, json);
return serialize_refund_response(env, pb::parse_refund(json_ref.view()));
});
}

extern "C"
JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_pro_BackendRequests_parseRevocationsResponse(
Expand Down
12 changes: 7 additions & 5 deletions library/src/main/cpp/pro_proof_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,22 @@ Java_network_loki_messenger_libsession_1util_pro_ProProof_nativeStatus(JNIEnv *e
jbyteArray signed_message_data,
jbyteArray signed_message_signature) {
return run_catching_cxx_exception_or_throws<jint>(env, [=]() {
std::optional<session::ProSignedMessage> signed_msg;
JavaByteArrayRef signed_message_data_ref(env, signed_message_data);
JavaByteArrayRef signed_message_signature_ref(env, signed_message_signature);

// §4: ProProof::status now takes an optional 64-byte user signature span + the message span
// directly (ProSignedMessage was removed).
std::optional<std::span<const std::uint8_t>> user_sig;
std::span<const std::uint8_t> signed_msg;
if (signed_message_data && signed_message_signature) {
signed_msg.emplace(session::ProSignedMessage {
.sig = signed_message_signature_ref.get(),
.msg = signed_message_data_ref.get(),
});
user_sig = signed_message_signature_ref.get();
signed_msg = signed_message_data_ref.get();
}

return static_cast<jint>(java_to_cpp_proof(env, thiz).status(
JavaByteArrayRef(env, verify_pub_key).get(),
std::chrono::sys_seconds{std::chrono::seconds(now_unix_ts)},
user_sig,
signed_msg
));
});
Expand Down
28 changes: 26 additions & 2 deletions library/src/main/cpp/protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ extern "C"
JNIEXPORT jbyteArray JNICALL
Java_network_loki_messenger_libsession_1util_protocol_SessionProtocol_encodeForCommunityInbox(
JNIEnv *env, jobject thiz, jbyteArray plaintext, jbyteArray my_ed25519_priv_key,
jlong timestamp_ms, jbyteArray recipient_pub_key, jbyteArray community_server_pub_key,
jlong /* timestamp_ms: removed from libsession — community-inbox carries no envelope ts */,
jbyteArray recipient_pub_key, jbyteArray community_server_pub_key,
jbyteArray rotating_key) {
return run_catching_cxx_exception_or_throws<jbyteArray>(env, [=] {
return util::bytes_from_vector(
env,
session::encode_for_community_inbox(
JavaByteArrayRef(env, plaintext).get(),
JavaByteArrayRef(env, my_ed25519_priv_key).get(),
std::chrono::milliseconds{timestamp_ms},
*java_to_cpp_array<33>(env, recipient_pub_key),
*java_to_cpp_array<32>(env, community_server_pub_key),
rotating_key ? std::optional(JavaByteArrayRef(env, rotating_key).get())
Expand Down Expand Up @@ -246,3 +246,27 @@ Java_network_loki_messenger_libsession_1util_protocol_SessionProtocol_decodeForG
)).release();
});
}

extern "C"
JNIEXPORT jlong JNICALL
Java_network_loki_messenger_libsession_1util_protocol_SessionProtocol_proFeaturesForMessage(
JNIEnv *env, jobject thiz, jint codepoint_count) {
return run_catching_cxx_exception_or_throws<jlong>(env, [=]() {
return static_cast<jlong>(
session::pro_features_for_message(static_cast<size_t>(codepoint_count)).bitset.data);
});
}

extern "C"
JNIEXPORT jint JNICALL
Java_network_loki_messenger_libsession_1util_protocol_SessionProtocol_standardCharacterLimit(
JNIEnv *env, jobject thiz) {
return static_cast<jint>(session::STANDARD_CHARACTER_LIMIT);
}

extern "C"
JNIEXPORT jint JNICALL
Java_network_loki_messenger_libsession_1util_protocol_SessionProtocol_proHigherCharacterLimit(
JNIEnv *env, jobject thiz) {
return static_cast<jint>(session::PRO_HIGHER_CHARACTER_LIMIT);
}
Loading
Loading