From fd1b1afd53740e3fe4fa2e2f17184e97dcc5d313 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 25 Aug 2026 11:36:32 +1000 Subject: [PATCH] Re-pin libsession-util 194752f0 -> ce827173 (dev tip, #130 merged) Picks up the removal of `ProProof::version` (f0b9ab23, in #130). A proof's format is bound into its signature by the 16-byte domain prefix that format selects -- `ProProof_v0_____` -- so the version was never part of the signed bytes and carrying it proved nothing. Protobuf tag 1 is retired and must not be reused; a future format arrives as its own field rather than a version bump. Not an inert bump this time: the glue mirrored the field on both sides of JNI, and both halves have to move together. - `ProProof.kt` drops `version` from the data class and from the `@Keep` constructor the JNI layer calls. - `pro_proof_util.cpp` drops the cached `getVersion` method id and the `.version` initialiser, and -- the part no compiler checks -- narrows the constructor signature from `(I[B[BJ[B)V` to `([B[BJ[B)V`. A stale signature there is not a build error, it is a `NoSuchMethodError` the first time a proof crosses the boundary. `ProStatus` gained a fifth case in the same range, `Invalid` (= 5): the message carried no proof this client can read, so it is delivered as non-Pro rather than dropped. Nothing in the glue switches on the status -- it is a `typealias ProProofStatus = Int` handed straight through -- but the companion object mirrors the C enum, so `STATUS_INVALID` is added to keep that mirror complete instead of leaving callers to hardcode 5. Note the intermediate commits in #130 called this case `UnsupportedVersion`; f0b9ab23 replaced it with the general `Invalid` before the merge, so `Invalid` is the name that landed. Nothing else in the bump reaches the glue: only `session_protocol.{h,hpp}` changed under `include/`, `ProProof::status()` keeps its signature, and the new `attachments::encrypt` input validation is on the `std::filesystem::path` overload, which this glue does not call. Co-Authored-By: Claude Opus 5 (1M context) --- library/src/main/cpp/pro_proof_util.cpp | 8 +++----- .../messenger/libsession_util/pro/ProProof.kt | 17 +++++++++++++---- libsession-util | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/library/src/main/cpp/pro_proof_util.cpp b/library/src/main/cpp/pro_proof_util.cpp index 3102ff4..2338eee 100644 --- a/library/src/main/cpp/pro_proof_util.cpp +++ b/library/src/main/cpp/pro_proof_util.cpp @@ -19,7 +19,6 @@ static std::array from_hex(std::span input) { session::ProProof java_to_cpp_proof(JNIEnv *env, jobject proof) { struct ProProofMethods : public JavaClassInfo { - jmethodID get_version; jmethodID get_revocation_tag; jmethodID get_rotating_pub_key; jmethodID get_expiry_seconds; @@ -27,7 +26,6 @@ session::ProProof java_to_cpp_proof(JNIEnv *env, jobject proof) { ProProofMethods(JNIEnv *env, jobject obj) : JavaClassInfo(env, obj) - , get_version(env->GetMethodID(java_class, "getVersion", "()I")) , get_revocation_tag(env->GetMethodID(java_class, "getRevocationTagHex", "()Ljava/lang/String;")) , get_rotating_pub_key(env->GetMethodID(java_class, "getRotatingPubKeyHex", "()Ljava/lang/String;")) , get_expiry_seconds(env->GetMethodID(java_class, "getExpirySeconds", "()J")) @@ -42,7 +40,6 @@ session::ProProof java_to_cpp_proof(JNIEnv *env, jobject proof) { jni_utils::JavaLocalRef signature(env, (jstring) env->CallObjectMethod(proof, methods.get_signature)); return { - .version = static_cast(env->CallIntMethod(proof, methods.get_version)), .revocation_tag = from_hex<32>(jni_utils::JavaStringRef(env, revocation_tag.get()).get()), .rotating_pubkey = from_hex<32>(jni_utils::JavaStringRef(env, rotating_pub_key.get()).get()), .expiry_at = std::chrono::sys_seconds( @@ -52,14 +49,15 @@ session::ProProof java_to_cpp_proof(JNIEnv *env, jobject proof) { } JavaLocalRef cpp_to_java_proof(JNIEnv *env, const session::ProProof &proof) { + // No leading `I`: ProProof carries no version. The proof's format is fixed by the signing + // domain prefix baked into its signature, not by a field, so there is nothing to marshal. static BasicJavaClassInfo class_info(env, "network/loki/messenger/libsession_util/pro/ProProof", - "(I[B[BJ[B)V"); + "([B[BJ[B)V"); return {env, env->NewObject( class_info.java_class, class_info.constructor, - static_cast(proof.version), util::bytes_from_span(env, proof.revocation_tag).get(), util::bytes_from_span(env, proof.rotating_pubkey).get(), static_cast(proof.expiry_at.time_since_epoch().count()), diff --git a/library/src/main/java/network/loki/messenger/libsession_util/pro/ProProof.kt b/library/src/main/java/network/loki/messenger/libsession_util/pro/ProProof.kt index eeba248..88c8c58 100644 --- a/library/src/main/java/network/loki/messenger/libsession_util/pro/ProProof.kt +++ b/library/src/main/java/network/loki/messenger/libsession_util/pro/ProProof.kt @@ -10,11 +10,15 @@ typealias ProProofStatus = Int /** * Represents a proof of Pro. This class is marked as @Serializable to represent the JSON structure * received from the Pro Backend. + * + * There is deliberately no `version` field. A proof's format is bound into its signature by the + * 16-byte domain prefix that format selects (`ProProof_v0_____`), so the version was never part of + * the signed bytes and carrying it proved nothing. A future format arrives as its own field rather + * than a version bump, so to a client that does not know it the proof simply does not appear — + * reported as [STATUS_INVALID]. Protobuf tag 1 held the old field and must never be reused. */ @Serializable data class ProProof( - val version: Int, - @SerialName("revocation_tag") val revocationTagHex: String, @@ -29,13 +33,11 @@ data class ProProof( ) { @Keep constructor( - version: Int, revocationTag: ByteArray, rotatingPubKey: ByteArray, expirySeconds: Long, signature: ByteArray ): this( - version = version, revocationTagHex = revocationTag.toHexString(), rotatingPubKeyHex = rotatingPubKey.toHexString(), expirySeconds = expirySeconds, @@ -92,5 +94,12 @@ data class ProProof( const val STATUS_INVALID_USER_SIGNATURE: ProProofStatus = 2 const val STATUS_VALID: ProProofStatus = 3 const val STATUS_EXPIRED: ProProofStatus = 4 + + /** + * Nothing could be evaluated, so the decoded proof is not to be trusted: the message + * carried no proof this client can read — either none was attached, or it is in a format + * this client does not know. Such a message is delivered as non-Pro rather than dropped. + */ + const val STATUS_INVALID: ProProofStatus = 5 } } \ No newline at end of file diff --git a/libsession-util b/libsession-util index 194752f..ce82717 160000 --- a/libsession-util +++ b/libsession-util @@ -1 +1 @@ -Subproject commit 194752f094818f092d9262bf3986f7d68c3d4ced +Subproject commit ce827173f295019021197ce54d5d051323490ecc