diff --git a/Cargo.toml b/Cargo.toml index 90d781eeef..79fbaa590f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "rust/protocol", - "rust/bridge/ffi" + "rust/bridge/ffi", + "rust/bridge/jni" ] diff --git a/rust/bridge/jni/Cargo.toml b/rust/bridge/jni/Cargo.toml new file mode 100644 index 0000000000..81d9503f44 --- /dev/null +++ b/rust/bridge/jni/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "libsignal-jni" +version = "0.1.0" +authors = ["Jack Lloyd "] +edition = "2018" + +[lib] +name = "signal_jni" +crate-type = ["dylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libsignal-protocol-rust = { path = "../../protocol" } +jni = "0.17" +rand = "0.7.3" diff --git a/rust/bridge/jni/README.md b/rust/bridge/jni/README.md new file mode 100644 index 0000000000..7335050452 --- /dev/null +++ b/rust/bridge/jni/README.md @@ -0,0 +1 @@ +# libsignal-jni diff --git a/rust/bridge/jni/src/lib.rs b/rust/bridge/jni/src/lib.rs new file mode 100644 index 0000000000..3dbdbd6106 --- /dev/null +++ b/rust/bridge/jni/src/lib.rs @@ -0,0 +1,1689 @@ +// +// Copyright (C) 2020 Signal Messenger, LLC. +// All rights reserved. +// +// SPDX-License-Identifier: GPL-3.0-only +// + +#![allow(clippy::missing_safety_doc)] +#![deny(warnings)] + +use jni::objects::{JClass, JObject, JString, JValue}; +use jni::sys::{jboolean, jbyteArray, jint, jlong, jobject, jstring}; +use jni::JNIEnv; +use libsignal_protocol_rust::*; +use std::convert::TryFrom; + +mod util; + +use crate::util::*; + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_SignalProtocolAddress_nativeNew( + env: JNIEnv, + _class: JClass, + name: JString, + device_id: jint, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let name: String = env.get_string(name)?.into(); + let device_id = jint_to_u32(device_id)?; + let address = ProtocolAddress::new(name, device_id); + box_object::(Ok(address)) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_SignalProtocolAddress_nativeDestroy destroys ProtocolAddress); + +jni_fn_get_jstring!(Java_org_whispersystems_libsignal_SignalProtocolAddress_nativeName(ProtocolAddress) using + |p: &ProtocolAddress| Ok(p.name().to_string())); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_SignalProtocolAddress_nativeDeviceId(ProtocolAddress) using + |obj: &ProtocolAddress| { Ok(obj.device_id()) }); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_ecc_ECPublicKey_nativeDeserialize( + env: JNIEnv, + _class: JClass, + data: jbyteArray, + offset: jint, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let offset = jint_to_u32(offset)? as usize; + let data = env.convert_byte_array(data)?; + let key = PublicKey::deserialize(&data[offset..])?; + box_object::(Ok(key)) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_ecc_ECPublicKey_nativeCompare( + env: JNIEnv, + _class: JClass, + key1: ObjectHandle, + key2: ObjectHandle, +) -> jint { + run_ffi_safe(&env, || { + let key1 = native_handle_cast::(key1)?; + let key2 = native_handle_cast::(key2)?; + + match key1.cmp(&key2) { + std::cmp::Ordering::Less => Ok(-1), + std::cmp::Ordering::Equal => Ok(0), + std::cmp::Ordering::Greater => Ok(1), + } + }) +} + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_ecc_ECPublicKey_nativeSerialize(PublicKey) using + |k: &PublicKey| Ok(k.serialize())); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_ecc_ECPublicKey_nativeVerify( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + message: jbyteArray, + signature: jbyteArray, +) -> jboolean { + run_ffi_safe(&env, || { + let key = native_handle_cast::(handle)?; + let message = env.convert_byte_array(message)?; + let signature = env.convert_byte_array(signature)?; + Ok(key.verify_signature(&message, &signature)? as jboolean) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_ecc_ECPublicKey_nativeDestroy destroys PublicKey); + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeDeserialize is PrivateKey::deserialize); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeSerialize(PrivateKey) using + |k: &PrivateKey| Ok(k.serialize())); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeGenerate( + env: JNIEnv, + _class: JClass, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let mut rng = rand::rngs::OsRng; + let keypair = KeyPair::generate(&mut rng); + box_object::(Ok(keypair.private_key)) + }) +} + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeGetPublicKey(PublicKey) from PrivateKey, + |k: &PrivateKey| k.public_key()); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeSign( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + message: jbyteArray, +) -> jbyteArray { + run_ffi_safe(&env, || { + let message = env.convert_byte_array(message)?; + let key = native_handle_cast::(handle)?; + let mut rng = rand::rngs::OsRng; + let sig = key.calculate_signature(&message, &mut rng)?; + to_jbytearray(&env, Ok(sig)) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeAgree( + env: JNIEnv, + _class: JClass, + private_key_handle: ObjectHandle, + public_key_handle: ObjectHandle, +) -> jbyteArray { + run_ffi_safe(&env, || { + let private_key = native_handle_cast::(private_key_handle)?; + let public_key = native_handle_cast::(public_key_handle)?; + let shared_secret = private_key.calculate_agreement(&public_key)?; + to_jbytearray(&env, Ok(shared_secret)) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_ecc_ECPrivateKey_nativeDestroy destroys PrivateKey); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_fingerprint_DisplayableFingerprint_nativeFormat( + env: JNIEnv, + _class: JClass, + local: jbyteArray, + remote: jbyteArray, +) -> jstring { + run_ffi_safe(&env, || { + let local = env.convert_byte_array(local)?; + let remote = env.convert_byte_array(remote)?; + let fingerprint = DisplayableFingerprint::new(&local, &remote)?; + let result = env.new_string(format!("{}", fingerprint))?; + Ok(result.into_inner()) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_fingerprint_NumericFingerprintGenerator_nativeNew( + env: JNIEnv, + _class: JClass, + iterations: jint, + version: jint, + local_identifier: jbyteArray, + local_key: jbyteArray, + remote_identifier: jbyteArray, + remote_key: jbyteArray, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let version = jint_to_u32(version)?; + let iterations = jint_to_u32(iterations)?; + + let local_identifier = env.convert_byte_array(local_identifier)?; + let local_key = env.convert_byte_array(local_key)?; + + let remote_identifier = env.convert_byte_array(remote_identifier)?; + let remote_key = env.convert_byte_array(remote_key)?; + + let local_key = IdentityKey::decode(&local_key)?; + let remote_key = IdentityKey::decode(&remote_key)?; + let fprint = Fingerprint::new( + version, + iterations, + &local_identifier, + &local_key, + &remote_identifier, + &remote_key, + )?; + + box_object::(Ok(fprint)) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_fingerprint_NumericFingerprintGenerator_nativeDestroy destroys Fingerprint); + +jni_fn_get_jstring!(Java_org_whispersystems_libsignal_fingerprint_NumericFingerprintGenerator_nativeGetDisplayString(Fingerprint) using + Fingerprint::display_string); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_fingerprint_NumericFingerprintGenerator_nativeGetScannableEncoding(Fingerprint) using + |f: &Fingerprint| f.scannable.serialize()); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_fingerprint_ScannableFingerprint_nativeCompare( + env: JNIEnv, + _class: JClass, + fprint1: jbyteArray, + fprint2: jbyteArray, +) -> jboolean { + run_ffi_safe(&env, || { + let fprint1 = env.convert_byte_array(fprint1)?; + let fprint2 = env.convert_byte_array(fprint2)?; + + let fprint1 = ScannableFingerprint::deserialize(&fprint1)?; + Ok(fprint1.compare(&fprint2)? as jboolean) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_kdf_HKDF_nativeDeriveSecrets( + env: JNIEnv, + _class: JClass, + version: jint, + input_key_material: jbyteArray, + salt: jbyteArray, + info: jbyteArray, + output_length: jint, +) -> jbyteArray { + run_ffi_safe(&env, || { + let version = jint_to_u32(version)?; + let output_length = output_length as usize; + + let input_key_material = env.convert_byte_array(input_key_material)?; + + let salt = if salt.is_null() { + None + } else { + Some(env.convert_byte_array(salt)?) + }; + + let info = env.convert_byte_array(info)?; + + let hkdf = HKDF::new(version)?; + let derived = if let Some(salt) = salt { + hkdf.derive_salted_secrets(&input_key_material, &salt, &info, output_length) + } else { + hkdf.derive_secrets(&input_key_material, &info, output_length) + }; + + to_jbytearray(&env, derived) + }) +} + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeDeserialize is SignalMessage::try_from); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeNew( + env: JNIEnv, + _class: JClass, + message_version: jint, + mac_key: jbyteArray, + sender_ratchet_key: ObjectHandle, + counter: jint, + previous_counter: jint, + ciphertext: jbyteArray, + sender_identity_key: ObjectHandle, + receiver_identity_key: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let message_version = jint_to_u8(message_version)?; + let mac_key = env.convert_byte_array(mac_key)?; + let sender_ratchet_key = native_handle_cast::(sender_ratchet_key)?; + let counter = jint_to_u32(counter)?; + let previous_counter = jint_to_u32(previous_counter)?; + let ciphertext = env.convert_byte_array(ciphertext)?; + + let sender_identity_key = native_handle_cast::(sender_identity_key)?; + let receiver_identity_key = native_handle_cast::(receiver_identity_key)?; + + let msg = SignalMessage::new( + message_version, + &mac_key, + *sender_ratchet_key, + counter, + previous_counter, + &ciphertext, + &IdentityKey::new(*sender_identity_key), + &IdentityKey::new(*receiver_identity_key), + )?; + + box_object::(Ok(msg)) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeDestroy destroys SignalMessage); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeGetSenderRatchetKey(SignalMessage) using + |m: &SignalMessage| Ok(m.sender_ratchet_key().serialize())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeGetBody(SignalMessage) using + |m: &SignalMessage| Ok(m.body().to_vec())); +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeGetSerialized(SignalMessage) using + |m: &SignalMessage| Ok(m.serialized().to_vec())); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeGetMessageVersion(SignalMessage) using + |msg: &SignalMessage| { Ok(msg.message_version() as u32) }); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeGetCounter(SignalMessage) using + |msg: &SignalMessage| { Ok(msg.counter()) }); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_SignalMessage_nativeVerifyMac( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + sender_identity_key: ObjectHandle, + receiver_identity_key: ObjectHandle, + mac_key: jbyteArray, +) -> jboolean { + run_ffi_safe(&env, || { + let msg = native_handle_cast::(handle)?; + let sender_identity_key = native_handle_cast::(sender_identity_key)?; + let receiver_identity_key = native_handle_cast::(receiver_identity_key)?; + let mac_key = env.convert_byte_array(mac_key)?; + + let valid = msg.verify_mac( + &IdentityKey::new(*sender_identity_key), + &IdentityKey::new(*receiver_identity_key), + &mac_key, + )?; + + Ok(valid as jboolean) + }) +} + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeDeserialize is PreKeySignalMessage::try_from); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeNew( + env: JNIEnv, + _class: JClass, + message_version: jint, + registration_id: jint, + pre_key_id: jint, + signed_pre_key_id: jint, + base_key_handle: ObjectHandle, + identity_key_handle: ObjectHandle, + signal_message_handle: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let message_version = message_version as u8; + let registration_id = jint_to_u32(registration_id)?; + let pre_key_id = if pre_key_id < 0 { + None + } else { + Some(jint_to_u32(pre_key_id)?) + }; + let signed_pre_key_id = jint_to_u32(signed_pre_key_id)?; + let base_key = native_handle_cast::(base_key_handle)?; + let identity_key = native_handle_cast::(identity_key_handle)?; + let signal_message = native_handle_cast::(signal_message_handle)?; + + let msg = PreKeySignalMessage::new( + message_version, + registration_id, + pre_key_id, + signed_pre_key_id, + *base_key, + IdentityKey::new(*identity_key), + signal_message.clone(), + ); + box_object::(msg) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeDestroy destroys PreKeySignalMessage); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetVersion(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.message_version() as u32)); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetRegistrationId(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.registration_id())); + +// Special logic to handle optionality: +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetPreKeyId( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, +) -> jint { + run_ffi_safe(&env, || { + let pksm = native_handle_cast::(handle)?; + match pksm.pre_key_id() { + Some(id) => jint_from_u32(Ok(id)), + None => Ok(-1 as jint), + } + }) +} + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetSignedPreKeyId(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.signed_pre_key_id())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetBaseKey(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.base_key().serialize())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetIdentityKey(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.identity_key().serialize())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetSignalMessage(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.message().serialized().to_vec())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_PreKeySignalMessage_nativeGetSerialized(PreKeySignalMessage) using + |m: &PreKeySignalMessage| Ok(m.serialized().to_vec())); + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeDeserialize is SenderKeyMessage::try_from); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeNew( + env: JNIEnv, + _class: JClass, + key_id: jint, + iteration: jint, + ciphertext: jbyteArray, + pk_handle: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let key_id = jint_to_u32(key_id)?; + let iteration = jint_to_u32(iteration)?; + let ciphertext = env.convert_byte_array(ciphertext)?; + let signature_key = native_handle_cast::(pk_handle)?; + let mut csprng = rand::rngs::OsRng; + let skm = SenderKeyMessage::new(key_id, iteration, &ciphertext, &mut csprng, signature_key); + box_object::(skm) + }) +} + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeDeserialize is SenderKeyDistributionMessage::try_from); + +jni_fn_destroy!(Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeDestroy destroys SenderKeyMessage); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeGetKeyId(SenderKeyMessage) using + |m: &SenderKeyMessage| Ok(m.key_id())); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeGetIteration(SenderKeyMessage) using + |m: &SenderKeyMessage| Ok(m.iteration())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeGetCipherText(SenderKeyMessage) using + |m: &SenderKeyMessage| Ok(m.ciphertext().to_vec())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeGetSerialized(SenderKeyMessage) using + |m: &SenderKeyMessage| Ok(m.serialized().to_vec())); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_SenderKeyMessage_nativeVerifySignature( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + pubkey_handle: ObjectHandle, +) -> jboolean { + run_ffi_safe(&env, || { + let skm = native_handle_cast::(handle)?; + let pubkey = native_handle_cast::(pubkey_handle)?; + let valid = skm.verify_signature(pubkey)?; + Ok(valid as jboolean) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeNew( + env: JNIEnv, + _class: JClass, + key_id: jint, + iteration: jint, + chainkey: jbyteArray, + pk_handle: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let key_id = jint_to_u32(key_id)?; + let iteration = jint_to_u32(iteration)?; + let chainkey = env.convert_byte_array(chainkey)?; + let signature_key = native_handle_cast::(pk_handle)?; + let skdm = SenderKeyDistributionMessage::new(key_id, iteration, &chainkey, *signature_key); + box_object::(skdm) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeDestroy destroys SenderKeyDistributionMessage); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeGetId(SenderKeyDistributionMessage) using + |m: &SenderKeyDistributionMessage| m.id()); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeGetIteration(SenderKeyDistributionMessage) using + |m: &SenderKeyDistributionMessage| m.iteration()); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeGetChainKey(SenderKeyDistributionMessage) using + |m: &SenderKeyDistributionMessage| Ok(m.chain_key()?.to_vec())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeGetSignatureKey(SenderKeyDistributionMessage) using + |m: &SenderKeyDistributionMessage| Ok(m.signing_key()?.serialize())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_protocol_SenderKeyDistributionMessage_nativeGetSerialized(SenderKeyDistributionMessage) using + |m: &SenderKeyDistributionMessage| Ok(m.serialized().to_vec())); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeNew( + env: JNIEnv, + _class: JClass, + registration_id: jint, + device_id: jint, + prekey_id: jint, + prekey_handle: ObjectHandle, + signed_prekey_id: jint, + signed_prekey_handle: ObjectHandle, + signed_prekey_signature: jbyteArray, + identity_key_handle: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let registration_id = jint_to_u32(registration_id)?; + let device_id = jint_to_u32(device_id)?; + let signed_prekey_id = jint_to_u32(signed_prekey_id)?; + let signed_prekey = native_handle_cast::(signed_prekey_handle)?; + let signed_prekey_signature = env.convert_byte_array(signed_prekey_signature)?; + + let prekey = native_handle_cast_optional::(prekey_handle)?.map(|k| *k); + + let prekey_id = if prekey_id < 0 { + None + } else { + Some(jint_to_u32(prekey_id)?) + }; + + let identity_key = IdentityKey::new(*(identity_key_handle as *mut PublicKey)); + + let bundle = PreKeyBundle::new( + registration_id, + device_id, + prekey_id, + prekey, + signed_prekey_id, + *signed_prekey, + signed_prekey_signature, + identity_key, + ); + + box_object::(bundle) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeDestroy destroys PreKeyBundle); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetRegistrationId(PreKeyBundle) using + |m: &PreKeyBundle| m.registration_id()); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetDeviceId(PreKeyBundle) using + |m: &PreKeyBundle| m.device_id()); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetSignedPreKeyId(PreKeyBundle) using + |m: &PreKeyBundle| m.signed_pre_key_id()); + +// Special logic for optional here: +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetPreKeyId( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, +) -> jint { + run_ffi_safe(&env, || { + let bundle = native_handle_cast::(handle)?; + match bundle.pre_key_id()? { + Some(prekey_id) => jint_from_u32(Ok(prekey_id)), + None => Ok(-1), + } + }) +} + +jni_fn_get_new_boxed_optional_obj!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetPreKeyPublic(PublicKey) from PreKeyBundle, + |p: &PreKeyBundle| p.pre_key_public()); + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetSignedPreKeyPublic(PublicKey) from PreKeyBundle, + |p: &PreKeyBundle| Ok(p.signed_pre_key_public()?)); + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetIdentityKey(PublicKey) from PreKeyBundle, + |p: &PreKeyBundle| Ok(*p.identity_key()?.public_key())); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_state_PreKeyBundle_nativeGetSignedPreKeySignature(PreKeyBundle) using + |m: &PreKeyBundle| Ok(m.signed_pre_key_signature()?.to_vec())); + +/* SignedPreKeyRecord */ + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeNew( + env: JNIEnv, + _class: JClass, + id: jint, + timestamp: jlong, + pub_key_handle: ObjectHandle, + priv_key_handle: ObjectHandle, + signature: jbyteArray, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let pub_key = native_handle_cast::(pub_key_handle)?; + let priv_key = native_handle_cast::(priv_key_handle)?; + let id = jint_to_u32(id)?; + let timestamp = timestamp as u64; + let keypair = KeyPair::new(*pub_key, *priv_key); + let signature = env.convert_byte_array(signature)?; + + let spkr = SignedPreKeyRecord::new(id, timestamp, &keypair, &signature); + + box_object::(Ok(spkr)) + }) +} + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeDeserialize is SignedPreKeyRecord::deserialize); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeGetId(SignedPreKeyRecord) using + |m: &SignedPreKeyRecord| m.id()); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeGetTimestamp( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, +) -> jlong { + run_ffi_safe(&env, || { + let spkr = native_handle_cast::(handle)?; + jlong_from_u64(spkr.timestamp()) + }) +} + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeGetPublicKey(PublicKey) from SignedPreKeyRecord, + |p: &SignedPreKeyRecord| p.public_key()); + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeGetPrivateKey(PrivateKey) from SignedPreKeyRecord, + |p: &SignedPreKeyRecord| p.private_key()); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeGetSignature(SignedPreKeyRecord) using + |m: &SignedPreKeyRecord| m.signature()); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeGetSerialized(SignedPreKeyRecord) using + |m: &SignedPreKeyRecord| m.serialize()); + +jni_fn_destroy!(Java_org_whispersystems_libsignal_state_SignedPreKeyRecord_nativeDestroy destroys SignedPreKeyRecord); + +/* PreKeyRecord */ + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeNew( + env: JNIEnv, + _class: JClass, + id: jint, + pub_key_handle: ObjectHandle, + priv_key_handle: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let id = jint_to_u32(id)?; + let pub_key = native_handle_cast::(pub_key_handle)?; + let priv_key = native_handle_cast::(priv_key_handle)?; + let keypair = KeyPair::new(*pub_key, *priv_key); + + let pkr = PreKeyRecord::new(id, &keypair); + + box_object::(Ok(pkr)) + }) +} + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeDeserialize is PreKeyRecord::deserialize); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeGetId(PreKeyRecord) using + |m: &PreKeyRecord| m.id()); + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeGetPublicKey(PublicKey) from PreKeyRecord, + |p: &PreKeyRecord| p.public_key()); + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeGetPrivateKey(PrivateKey) from PreKeyRecord, + |p: &PreKeyRecord| p.private_key()); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeGetSerialized(PreKeyRecord) using + |m: &PreKeyRecord| m.serialize()); + +jni_fn_destroy!(Java_org_whispersystems_libsignal_state_PreKeyRecord_nativeDestroy destroys PreKeyRecord); + +/* SenderKeyName */ + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_groups_SenderKeyName_nativeNew( + env: JNIEnv, + _class: JClass, + group_id: JString, + sender_name: JString, + sender_device_id: jint, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let group_id: String = env.get_string(group_id)?.into(); + let sender_name = env.get_string(sender_name)?.into(); + let sender_id = jint_to_u32(sender_device_id)?; + let name = SenderKeyName::new(group_id, ProtocolAddress::new(sender_name, sender_id)); + box_object::(name) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_groups_SenderKeyName_nativeDestroy destroys SenderKeyName); + +jni_fn_get_jstring!(Java_org_whispersystems_libsignal_groups_SenderKeyName_nativeGetGroupId(SenderKeyName) using + SenderKeyName::group_id); + +jni_fn_get_jstring!(Java_org_whispersystems_libsignal_groups_SenderKeyName_nativeGetSenderName(SenderKeyName) using + |skn: &SenderKeyName| { Ok(skn.sender()?.name().to_string()) }); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_groups_SenderKeyName_nativeGetSenderDeviceId(SenderKeyName) using + |m: &SenderKeyName| Ok(m.sender()?.device_id())); + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeNew( + env: JNIEnv, + _class: JClass, + id: jint, + iteration: jint, + chain_key: jbyteArray, + signature_public: ObjectHandle, + signature_private: ObjectHandle, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let id = jint_to_u32(id)?; + let iteration = jint_to_u32(iteration)?; + let chain_key = env.convert_byte_array(chain_key)?; + let signature_public = native_handle_cast::(signature_public)?; + let signature_private = + native_handle_cast_optional::(signature_private)?.map(|k| *k); + + let sks = SenderKeyState::new( + id, + iteration, + &chain_key, + *signature_public, + signature_private, + ); + box_object::(sks) + }) +} + +jni_fn_destroy!(Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeDestroy destroys SenderKeyState); + +jni_fn_deserialize!(Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeDeserialize is SenderKeyState::deserialize); + +jni_fn_get_jbytearray!(Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeGetSerialized(SenderKeyState) using + |sks: &SenderKeyState| sks.serialize()); + +jni_fn_get_jint!(Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeGetKeyId(SenderKeyState) using + |sks: &SenderKeyState| sks.sender_key_id()); + +jni_fn_get_new_boxed_obj!(Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeGetSigningKeyPublic(PublicKey) from SenderKeyState, + |sks: &SenderKeyState| sks.signing_key_public()); + +jni_fn_get_new_boxed_optional_obj!(Java_org_whispersystems_libsignal_groups_state_SenderKeyState_nativeGetSigningKeyPrivate(PrivateKey) from SenderKeyState, + |sks: &SenderKeyState| sks.signing_key_private()); + +fn sender_key_name_to_jobject<'a>( + env: &'a JNIEnv, + sender_key_name: &SenderKeyName, +) -> Result, SignalJniError> { + let sender_key_name_class = + env.find_class("org/whispersystems/libsignal/groups/SenderKeyName")?; + let sender_key_name_ctor_args = [ + JObject::from(env.new_string(sender_key_name.group_id()?)?).into(), + JObject::from(env.new_string(sender_key_name.sender_name()?)?).into(), + JValue::from(jint_from_u32(sender_key_name.sender_device_id())?), + ]; + + let sender_key_name_ctor_sig = "(Ljava/lang/String;Ljava/lang/String;I)V"; + let sender_key_name_jobject = env.new_object( + sender_key_name_class, + sender_key_name_ctor_sig, + &sender_key_name_ctor_args, + )?; + Ok(sender_key_name_jobject) +} + +fn protocol_address_to_jobject<'a>( + env: &'a JNIEnv, + address: &ProtocolAddress, +) -> Result, SignalJniError> { + let address_class = env.find_class("org/whispersystems/libsignal/SignalProtocolAddress")?; + let address_ctor_args = [ + JObject::from(env.new_string(address.name())?).into(), + JValue::from(jint_from_u32(Ok(address.device_id()))?), + ]; + + let address_ctor_sig = "(Ljava/lang/String;I)V"; + let address_jobject = env.new_object(address_class, address_ctor_sig, &address_ctor_args)?; + Ok(address_jobject) +} + +pub struct JniIdentityKeyStore<'a> { + env: &'a JNIEnv<'a>, + store: jobject, +} + +impl<'a> JniIdentityKeyStore<'a> { + fn new(env: &'a JNIEnv, store: jobject) -> Self { + Self { env, store } + } +} + +impl<'a> JniIdentityKeyStore<'a> { + fn do_get_identity_key_pair(&self) -> Result { + let callback_sig = "()Lorg/whispersystems/libsignal/IdentityKeyPair;"; + let bits = get_object_with_serialization( + self.env, + self.store, + &[], + callback_sig, + "getIdentityKeyPair", + )?; + + match bits { + None => Err(SignalJniError::Signal(SignalProtocolError::InternalError( + "getIdentityKeyPair returned null", + ))), + Some(k) => Ok(IdentityKeyPair::try_from(k.as_ref())?), + } + } + + fn do_get_local_registration_id(&self) -> Result { + let callback_sig = "()I"; + + let rvalue = + self.env + .call_method(self.store, "getLocalRegistrationId", callback_sig, &[])?; + exception_check(self.env, "getLocalRegistrationId")?; + + match rvalue { + JValue::Int(i) => jint_to_u32(i), + _ => Err(SignalJniError::UnexpectedJniResultType( + "getLocalRegistrationId", + rvalue.type_name(), + )), + } + } + + fn do_save_identity( + &mut self, + address: &ProtocolAddress, + identity: &IdentityKey, + ) -> Result { + let address_jobject = protocol_address_to_jobject(self.env, address)?; + let key_jobject = jobject_from_serialized( + self.env, + "org/whispersystems/libsignal/IdentityKey", + identity.serialize().as_ref(), + )?; + let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;Lorg/whispersystems/libsignal/IdentityKey;)Z"; + let callback_args = [address_jobject.into(), key_jobject.into()]; + let result = + self.env + .call_method(self.store, "saveIdentity", callback_sig, &callback_args)?; + exception_check(self.env, "saveIdentity")?; + + match result { + JValue::Bool(b) => Ok(b != 0), + _ => Err(SignalJniError::UnexpectedJniResultType( + "saveIdentity", + result.type_name(), + )), + } + } + + fn do_is_trusted_identity( + &self, + address: &ProtocolAddress, + identity: &IdentityKey, + direction: Direction, + ) -> Result { + let address_jobject = protocol_address_to_jobject(self.env, address)?; + let key_jobject = jobject_from_serialized( + self.env, + "org/whispersystems/libsignal/IdentityKey", + identity.serialize().as_ref(), + )?; + + let direction_class = self + .env + .find_class("org/whispersystems/libsignal/state/IdentityKeyStore$Direction")?; + let field_name = match direction { + Direction::Sending => "SENDING", + Direction::Receiving => "RECEIVING", + }; + + let field_value = self.env.get_static_field( + direction_class, + field_name, + "Lorg/whispersystems/libsignal/state/IdentityKeyStore$Direction;", + )?; + + let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;Lorg/whispersystems/libsignal/IdentityKey;Lorg/whispersystems/libsignal/state/IdentityKeyStore$Direction;)Z"; + let callback_args = [address_jobject.into(), key_jobject.into(), field_value]; + let result = self.env.call_method( + self.store, + "isTrustedIdentity", + callback_sig, + &callback_args, + )?; + exception_check(self.env, "isTrustedIdentity")?; + + match result { + JValue::Bool(b) => Ok(b != 0), + _ => Err(SignalJniError::UnexpectedJniResultType( + "isTrustedIdentity", + result.type_name(), + )), + } + } + + fn do_get_identity( + &self, + address: &ProtocolAddress, + ) -> Result, SignalJniError> { + let address_jobject = protocol_address_to_jobject(self.env, address)?; + let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;)Lorg/whispersystems/libsignal/IdentityKey;"; + let callback_args = [address_jobject.into()]; + + let bits = get_object_with_serialization( + self.env, + self.store, + &callback_args, + callback_sig, + "getIdentity", + )?; + + match bits { + None => Ok(None), + Some(k) => Ok(Some(IdentityKey::decode(&k)?)), + } + } +} + +impl<'a> IdentityKeyStore for JniIdentityKeyStore<'a> { + fn get_identity_key_pair(&self, _ctx: Context) -> Result { + Ok(self.do_get_identity_key_pair()?) + } + + fn get_local_registration_id(&self, _ctx: Context) -> Result { + Ok(self.do_get_local_registration_id()?) + } + + fn save_identity( + &mut self, + address: &ProtocolAddress, + identity: &IdentityKey, + _ctx: Context, + ) -> Result { + Ok(self.do_save_identity(address, identity)?) + } + + fn is_trusted_identity( + &self, + address: &ProtocolAddress, + identity: &IdentityKey, + direction: Direction, + _ctx: Context, + ) -> Result { + Ok(self.do_is_trusted_identity(address, identity, direction)?) + } + + fn get_identity( + &self, + address: &ProtocolAddress, + _ctx: Context, + ) -> Result, SignalProtocolError> { + Ok(self.do_get_identity(address)?) + } +} + +pub struct JniPreKeyStore<'a> { + env: &'a JNIEnv<'a>, + store: jobject, +} + +impl<'a> JniPreKeyStore<'a> { + fn new(env: &'a JNIEnv, store: jobject) -> Self { + Self { env, store } + } +} + +impl<'a> JniPreKeyStore<'a> { + fn do_get_pre_key(&self, prekey_id: u32) -> Result { + let callback_sig = "(I)Lorg/whispersystems/libsignal/state/PreKeyRecord;"; + let callback_args = [JValue::from(jint_from_u32(Ok(prekey_id))?)]; + let pk = get_object_with_native_handle::( + self.env, + self.store, + &callback_args, + callback_sig, + "loadPreKey", + )?; + match pk { + Some(pk) => Ok(pk), + None => Err(SignalJniError::Signal(SignalProtocolError::InvalidPreKeyId)), + } + } + + fn do_save_pre_key( + &mut self, + prekey_id: u32, + record: &PreKeyRecord, + ) -> Result<(), SignalJniError> { + let jobject_record = jobject_from_serialized( + self.env, + "org/whispersystems/libsignal/state/PreKeyRecord", + &record.serialize()?, + )?; + let callback_sig = "(I,Lorg/whispersystems/libsignal/state/PreKeyRecord;)V"; + let callback_args = [ + JValue::from(jint_from_u32(Ok(prekey_id))?), + jobject_record.into(), + ]; + self.env + .call_method(self.store, "storePreKey", callback_sig, &callback_args)?; + exception_check(self.env, "storePreKey")?; + Ok(()) + } + + fn do_remove_pre_key(&mut self, prekey_id: u32) -> Result<(), SignalJniError> { + let callback_sig = "(I)V"; + let callback_args = [JValue::from(jint_from_u32(Ok(prekey_id))?)]; + self.env + .call_method(self.store, "removePreKey", callback_sig, &callback_args)?; + exception_check(self.env, "removePreKey")?; + Ok(()) + } +} + +impl<'a> PreKeyStore for JniPreKeyStore<'a> { + fn get_pre_key( + &self, + prekey_id: u32, + _ctx: Context, + ) -> Result { + Ok(self.do_get_pre_key(prekey_id)?) + } + + fn save_pre_key( + &mut self, + prekey_id: u32, + record: &PreKeyRecord, + _ctx: Context, + ) -> Result<(), SignalProtocolError> { + Ok(self.do_save_pre_key(prekey_id, record)?) + } + + fn remove_pre_key(&mut self, prekey_id: u32, _ctx: Context) -> Result<(), SignalProtocolError> { + Ok(self.do_remove_pre_key(prekey_id)?) + } +} + +pub struct JniSignedPreKeyStore<'a> { + env: &'a JNIEnv<'a>, + store: jobject, +} + +impl<'a> JniSignedPreKeyStore<'a> { + fn new(env: &'a JNIEnv, store: jobject) -> Self { + Self { env, store } + } +} + +impl<'a> JniSignedPreKeyStore<'a> { + fn do_get_signed_pre_key(&self, prekey_id: u32) -> Result { + let callback_sig = "(I)Lorg/whispersystems/libsignal/state/SignedPreKeyRecord;"; + let callback_args = [JValue::from(jint_from_u32(Ok(prekey_id))?)]; + let spk = get_object_with_native_handle::( + self.env, + self.store, + &callback_args, + callback_sig, + "loadSignedPreKey", + )?; + match spk { + Some(spk) => Ok(spk), + None => Err(SignalJniError::Signal( + SignalProtocolError::InvalidSignedPreKeyId, + )), + } + } + + fn do_save_signed_pre_key( + &mut self, + prekey_id: u32, + record: &SignedPreKeyRecord, + ) -> Result<(), SignalJniError> { + let jobject_record = jobject_from_serialized( + self.env, + "org/whispersystems/libsignal/state/SignedPreKeyRecord", + &record.serialize()?, + )?; + let callback_sig = "(I,Lorg/whispersystems/libsignal/state/SignedPreKeyRecord;)V"; + let callback_args = [ + JValue::from(jint_from_u32(Ok(prekey_id))?), + jobject_record.into(), + ]; + self.env.call_method( + self.store, + "storeSignedPreKey", + callback_sig, + &callback_args, + )?; + exception_check(self.env, "storeSignedPreKey")?; + Ok(()) + } +} + +impl<'a> SignedPreKeyStore for JniSignedPreKeyStore<'a> { + fn get_signed_pre_key( + &self, + prekey_id: u32, + _ctx: Context, + ) -> Result { + Ok(self.do_get_signed_pre_key(prekey_id)?) + } + + fn save_signed_pre_key( + &mut self, + prekey_id: u32, + record: &SignedPreKeyRecord, + _ctx: Context, + ) -> Result<(), SignalProtocolError> { + Ok(self.do_save_signed_pre_key(prekey_id, record)?) + } +} + +pub struct JniSessionStore<'a> { + env: &'a JNIEnv<'a>, + store: jobject, +} + +impl<'a> JniSessionStore<'a> { + fn new(env: &'a JNIEnv, store: jobject) -> Self { + Self { env, store } + } +} + +impl<'a> JniSessionStore<'a> { + fn do_load_session( + &self, + address: &ProtocolAddress, + ) -> Result, SignalJniError> { + let address_jobject = protocol_address_to_jobject(self.env, address)?; + + let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;)Lorg/whispersystems/libsignal/state/SessionRecord;"; + let callback_args = [address_jobject.into()]; + let session = get_object_with_serialization( + self.env, + self.store, + &callback_args, + callback_sig, + "loadSession", + )?; + + match session { + None => Ok(None), + Some(s) => Ok(Some(SessionRecord::deserialize(&s)?)), + } + } + + fn do_store_session( + &mut self, + address: &ProtocolAddress, + record: &SessionRecord, + ) -> Result<(), SignalJniError> { + let address_jobject = protocol_address_to_jobject(self.env, address)?; + let session_jobject = jobject_from_serialized( + self.env, + "org/whispersystems/libsignal/state/SessionRecord", + &record.serialize()?, + )?; + + let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;Lorg/whispersystems/libsignal/state/SessionRecord;)V"; + let callback_args = [address_jobject.into(), session_jobject.into()]; + self.env + .call_method(self.store, "storeSession", callback_sig, &callback_args)?; + exception_check(self.env, "storeSession")?; + Ok(()) + } +} + +impl<'a> SessionStore for JniSessionStore<'a> { + fn load_session( + &self, + address: &ProtocolAddress, + _ctx: Context, + ) -> Result, SignalProtocolError> { + Ok(self.do_load_session(address)?) + } + + fn store_session( + &mut self, + address: &ProtocolAddress, + record: &SessionRecord, + _ctx: Context, + ) -> Result<(), SignalProtocolError> { + Ok(self.do_store_session(address, record)?) + } +} + +fn create_identity_store<'a>( + env: &'a JNIEnv, + obj: jobject, +) -> Result, SignalJniError> { + let identity_key_store = check_jobject_type( + &env, + obj, + "org/whispersystems/libsignal/state/IdentityKeyStore", + )?; + Ok(JniIdentityKeyStore::new(&env, identity_key_store)) +} + +fn create_session_store<'a>( + env: &'a JNIEnv, + obj: jobject, +) -> Result, SignalJniError> { + let session_store = + check_jobject_type(&env, obj, "org/whispersystems/libsignal/state/SessionStore")?; + Ok(JniSessionStore::new(&env, session_store)) +} + +fn create_prekey_store<'a>( + env: &'a JNIEnv, + obj: jobject, +) -> Result, SignalJniError> { + let prekey_store = + check_jobject_type(&env, obj, "org/whispersystems/libsignal/state/PreKeyStore")?; + Ok(JniPreKeyStore::new(&env, prekey_store)) +} + +fn create_signed_prekey_store<'a>( + env: &'a JNIEnv, + obj: jobject, +) -> Result, SignalJniError> { + let signed_prekey_store = check_jobject_type( + &env, + obj, + "org/whispersystems/libsignal/state/SignedPreKeyStore", + )?; + Ok(JniSignedPreKeyStore::new(&env, signed_prekey_store)) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_SessionBuilder_nativeProcessPreKeyBundle( + env: JNIEnv, + _class: JClass, + bundle: ObjectHandle, + protocol_address: ObjectHandle, + session_store: jobject, + identity_key_store: jobject, +) { + run_ffi_safe(&env, || { + let bundle = native_handle_cast::(bundle)?; + let protocol_address = native_handle_cast::(protocol_address)?; + + let mut identity_key_store = create_identity_store(&env, identity_key_store)?; + let mut session_store = create_session_store(&env, session_store)?; + + let mut csprng = rand::rngs::OsRng; + process_prekey_bundle( + &protocol_address, + &mut session_store, + &mut identity_key_store, + bundle, + &mut csprng, + None, + )?; + + Ok(()) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_SessionCipher_nativeEncryptMessage( + env: JNIEnv, + _class: JClass, + message: jbyteArray, + protocol_address: ObjectHandle, + session_store: jobject, + identity_key_store: jobject, +) -> jobject { + run_ffi_safe(&env, || { + let message = env.convert_byte_array(message)?; + let protocol_address = native_handle_cast::(protocol_address)?; + + let mut identity_key_store = create_identity_store(&env, identity_key_store)?; + let mut session_store = create_session_store(&env, session_store)?; + + let ctext = message_encrypt( + &message, + &protocol_address, + &mut session_store, + &mut identity_key_store, + None, + )?; + + let obj = match ctext { + CiphertextMessage::SignalMessage(m) => jobject_from_native_handle( + &env, + "org/whispersystems/libsignal/protocol/SignalMessage", + box_object::(Ok(m))?, + ), + CiphertextMessage::PreKeySignalMessage(m) => jobject_from_native_handle( + &env, + "org/whispersystems/libsignal/protocol/PreKeySignalMessage", + box_object::(Ok(m))?, + ), + _ => Err(SignalJniError::Signal(SignalProtocolError::InternalError( + "Unexpected result type from message_encrypt", + ))), + }; + + Ok(obj?.into_inner()) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_SessionCipher_nativeDecryptSignalMessage( + env: JNIEnv, + _class: JClass, + message: ObjectHandle, + protocol_address: ObjectHandle, + session_store: jobject, + identity_key_store: jobject, +) -> jbyteArray { + run_ffi_safe(&env, || { + let message = native_handle_cast::(message)?; + let protocol_address = native_handle_cast::(protocol_address)?; + + let mut identity_key_store = create_identity_store(&env, identity_key_store)?; + let mut session_store = create_session_store(&env, session_store)?; + + let mut csprng = rand::rngs::OsRng; + let ptext = message_decrypt_signal( + &message, + &protocol_address, + &mut session_store, + &mut identity_key_store, + &mut csprng, + None, + )?; + + to_jbytearray(&env, Ok(ptext)) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_SessionCipher_nativeDecryptPreKeySignalMessage( + env: JNIEnv, + _class: JClass, + message: ObjectHandle, + protocol_address: ObjectHandle, + session_store: jobject, + identity_key_store: jobject, + prekey_store: jobject, + signed_prekey_store: jobject, +) -> jbyteArray { + run_ffi_safe(&env, || { + let message = native_handle_cast::(message)?; + let protocol_address = native_handle_cast::(protocol_address)?; + let mut identity_key_store = create_identity_store(&env, identity_key_store)?; + let mut session_store = create_session_store(&env, session_store)?; + let mut prekey_store = create_prekey_store(&env, prekey_store)?; + let mut signed_prekey_store = create_signed_prekey_store(&env, signed_prekey_store)?; + + let mut csprng = rand::rngs::OsRng; + let ptext = message_decrypt_prekey( + &message, + &protocol_address, + &mut session_store, + &mut identity_key_store, + &mut prekey_store, + &mut signed_prekey_store, + &mut csprng, + None, + )?; + + to_jbytearray(&env, Ok(ptext)) + }) +} + +pub struct JniSenderKeyStore<'a> { + env: &'a JNIEnv<'a>, + store: jobject, +} + +impl<'a> JniSenderKeyStore<'a> { + fn new(env: &'a JNIEnv, store: jobject) -> Self { + Self { env, store } + } +} + +impl<'a> JniSenderKeyStore<'a> { + fn do_store_sender_key( + &mut self, + sender_key_name: &SenderKeyName, + record: &SenderKeyRecord, + ) -> Result<(), SignalJniError> { + let sender_key_name_jobject = sender_key_name_to_jobject(self.env, sender_key_name)?; + let sender_key_record_jobject = jobject_from_serialized( + self.env, + "org/whispersystems/libsignal/groups/state/SenderKeyRecord", + &record.serialize()?, + )?; + + let callback_args = [ + sender_key_name_jobject.into(), + sender_key_record_jobject.into(), + ]; + let callback_sig = "(Lorg/whispersystems/libsignal/groups/SenderKeyName;Lorg/whispersystems/libsignal/groups/state/SenderKeyRecord;)V"; + self.env.call_method( + self.store, + "storeSenderKey", + callback_sig, + &callback_args[..], + )?; + exception_check(self.env, "storeSenderKey")?; + + Ok(()) + } + + fn do_load_sender_key( + &mut self, + sender_key_name: &SenderKeyName, + ) -> Result, SignalJniError> { + let sender_key_name_jobject = sender_key_name_to_jobject(self.env, sender_key_name)?; + let callback_args = [sender_key_name_jobject.into()]; + let callback_sig = "(Lorg/whispersystems/libsignal/groups/SenderKeyName;)Lorg/whispersystems/libsignal/groups/state/SenderKeyRecord;"; + + let skr = get_object_with_serialization( + self.env, + self.store, + &callback_args, + callback_sig, + "loadSenderKey", + )?; + + match skr { + None => Ok(None), + Some(k) => Ok(Some(SenderKeyRecord::deserialize(&k)?)), + } + } +} + +impl<'a> SenderKeyStore for JniSenderKeyStore<'a> { + fn store_sender_key( + &mut self, + sender_key_name: &SenderKeyName, + record: &SenderKeyRecord, + _ctx: Context, + ) -> Result<(), SignalProtocolError> { + Ok(self.do_store_sender_key(sender_key_name, record)?) + } + + fn load_sender_key( + &mut self, + sender_key_name: &SenderKeyName, + _ctx: Context, + ) -> Result, SignalProtocolError> { + Ok(self.do_load_sender_key(sender_key_name)?) + } +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_groups_GroupSessionBuilder_nativeCreateSenderKeyDistributionMessage( + env: JNIEnv, + _class: JClass, + sender_key_name: ObjectHandle, + store: jobject, +) -> ObjectHandle { + run_ffi_safe(&env, || { + let sender_key_name = native_handle_cast::(sender_key_name)?; + let store = check_jobject_type( + &env, + store, + "org/whispersystems/libsignal/groups/state/SenderKeyStore", + )?; + + let mut sender_key_store = JniSenderKeyStore::new(&env, store); + let mut csprng = rand::rngs::OsRng; + + let skdm = create_sender_key_distribution_message( + &sender_key_name, + &mut sender_key_store, + &mut csprng, + None, + )?; + box_object::(Ok(skdm)) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_groups_GroupSessionBuilder_nativeProcessSenderKeyDistributionMessage( + env: JNIEnv, + _class: JClass, + sender_key_name: ObjectHandle, + sender_key_distribution_message: ObjectHandle, + store: jobject, +) { + run_ffi_safe(&env, || { + let sender_key_name = native_handle_cast::(sender_key_name)?; + let sender_key_distribution_message = + native_handle_cast::(sender_key_distribution_message)?; + let store = check_jobject_type( + &env, + store, + "org/whispersystems/libsignal/groups/state/SenderKeyStore", + )?; + + let mut sender_key_store = JniSenderKeyStore::new(&env, store); + + process_sender_key_distribution_message( + sender_key_name, + sender_key_distribution_message, + &mut sender_key_store, + None, + )?; + Ok(()) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_groups_GroupCipher_nativeEncryptMessage( + env: JNIEnv, + _class: JClass, + sender_key_name: ObjectHandle, + message: jbyteArray, + store: jobject, +) -> jbyteArray { + run_ffi_safe(&env, || { + let sender_key_name = native_handle_cast::(sender_key_name)?; + let message = env.convert_byte_array(message)?; + let store = check_jobject_type( + &env, + store, + "org/whispersystems/libsignal/groups/state/SenderKeyStore", + )?; + + let mut sender_key_store = JniSenderKeyStore::new(&env, store); + + let mut rng = rand::rngs::OsRng; + + let ctext = group_encrypt( + &mut sender_key_store, + &sender_key_name, + &message, + &mut rng, + None, + )?; + + to_jbytearray(&env, Ok(ctext)) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_groups_GroupCipher_nativeDecryptMessage( + env: JNIEnv, + _class: JClass, + sender_key_name: ObjectHandle, + message: jbyteArray, + store: jobject, +) -> jbyteArray { + run_ffi_safe(&env, || { + let sender_key_name = native_handle_cast::(sender_key_name)?; + let message = env.convert_byte_array(message)?; + let store = check_jobject_type( + &env, + store, + "org/whispersystems/libsignal/groups/state/SenderKeyStore", + )?; + + let mut sender_key_store = JniSenderKeyStore::new(&env, store); + + let ptext = group_decrypt(&message, &mut sender_key_store, &sender_key_name, None)?; + + to_jbytearray(&env, Ok(ptext)) + }) +} + +// The following are just exposed to make it possible to retain some of the Java tests: + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_SessionState_nativeInitializeAliceSession( + env: JNIEnv, + _class: JClass, + identity_key_private: ObjectHandle, + identity_key_public: ObjectHandle, + base_private: ObjectHandle, + base_public: ObjectHandle, + their_identity_key: ObjectHandle, + their_signed_prekey: ObjectHandle, + their_ratchet_key: ObjectHandle, +) -> jbyteArray { + run_ffi_safe(&env, || { + let identity_key_private = native_handle_cast::(identity_key_private)?; + let identity_key_public = native_handle_cast::(identity_key_public)?; + let base_private = native_handle_cast::(base_private)?; + let base_public = native_handle_cast::(base_public)?; + let their_identity_key = native_handle_cast::(their_identity_key)?; + let their_signed_prekey = native_handle_cast::(their_signed_prekey)?; + let their_ratchet_key = native_handle_cast::(their_ratchet_key)?; + + let our_identity_key_pair = IdentityKeyPair::new( + IdentityKey::new(*identity_key_public), + *identity_key_private, + ); + + let our_base_key_pair = KeyPair::new(*base_public, *base_private); + + let their_identity_key = IdentityKey::new(*their_identity_key); + + let mut csprng = rand::rngs::OsRng; + + let parameters = AliceSignalProtocolParameters::new( + our_identity_key_pair, + our_base_key_pair, + their_identity_key, + *their_signed_prekey, + None, + *their_ratchet_key, + ); + + let session = initialize_alice_session(¶meters, &mut csprng)?; + + to_jbytearray(&env, session.serialize()) + }) +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_whispersystems_libsignal_state_SessionState_nativeInitializeBobSession( + env: JNIEnv, + _class: JClass, + identity_key_private: ObjectHandle, + identity_key_public: ObjectHandle, + signed_prekey_private: ObjectHandle, + signed_prekey_public: ObjectHandle, + eph_private: ObjectHandle, + eph_public: ObjectHandle, + their_identity_key: ObjectHandle, + their_base_key: ObjectHandle, +) -> jbyteArray { + run_ffi_safe(&env, || { + let identity_key_private = native_handle_cast::(identity_key_private)?; + let identity_key_public = native_handle_cast::(identity_key_public)?; + let signed_prekey_private = native_handle_cast::(signed_prekey_private)?; + let signed_prekey_public = native_handle_cast::(signed_prekey_public)?; + let eph_private = native_handle_cast::(eph_private)?; + let eph_public = native_handle_cast::(eph_public)?; + let their_identity_key = native_handle_cast::(their_identity_key)?; + let their_base_key = native_handle_cast::(their_base_key)?; + + let our_identity_key_pair = IdentityKeyPair::new( + IdentityKey::new(*identity_key_public), + *identity_key_private, + ); + + let our_signed_pre_key_pair = KeyPair::new(*signed_prekey_public, *signed_prekey_private); + + let our_ratchet_key_pair = KeyPair::new(*eph_public, *eph_private); + + let their_identity_key = IdentityKey::new(*their_identity_key); + + let parameters = BobSignalProtocolParameters::new( + our_identity_key_pair, + our_signed_pre_key_pair, + None, + our_ratchet_key_pair, + their_identity_key, + *their_base_key, + ); + + let session = initialize_bob_session(¶meters)?; + + to_jbytearray(&env, session.serialize()) + }) +} diff --git a/rust/bridge/jni/src/util.rs b/rust/bridge/jni/src/util.rs new file mode 100644 index 0000000000..c6cdfb31af --- /dev/null +++ b/rust/bridge/jni/src/util.rs @@ -0,0 +1,630 @@ +// +// Copyright (C) 2020 Signal Messenger, LLC. +// All rights reserved. +// +// SPDX-License-Identifier: GPL-3.0-only +// + +use jni::objects::{JObject, JString, JThrowable, JValue}; +use jni::sys::{_jobject, jboolean, jbyteArray, jint, jlong, jobject, jstring}; +use jni::JNIEnv; +use libsignal_protocol_rust::SignalProtocolError; +use std::fmt; + +#[derive(Debug)] +pub enum SignalJniError { + Signal(SignalProtocolError), + Jni(jni::errors::Error), + BadJniParameter(&'static str), + UnexpectedJniResultType(&'static str, &'static str), + NullHandle, + IntegerOverflow(String), + UnexpectedPanic(std::boxed::Box), + ExceptionDuringCallback(String), +} + +impl SignalJniError { + pub fn to_signal_protocol_error(&self) -> SignalProtocolError { + match self { + SignalJniError::Signal(e) => e.clone(), + SignalJniError::Jni(e) => SignalProtocolError::FfiBindingError(e.to_string()), + SignalJniError::BadJniParameter(m) => { + SignalProtocolError::InvalidArgument(m.to_string()) + } + _ => SignalProtocolError::FfiBindingError(format!("{}", self)), + } + } +} + +impl fmt::Display for SignalJniError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SignalJniError::Signal(s) => write!(f, "{}", s), + SignalJniError::Jni(s) => write!(f, "JNI error {}", s), + SignalJniError::ExceptionDuringCallback(s) => { + write!(f, "exception recieved during callback {}", s) + } + SignalJniError::NullHandle => write!(f, "null handle"), + SignalJniError::BadJniParameter(m) => write!(f, "bad parameter type {}", m), + SignalJniError::UnexpectedJniResultType(m, t) => { + write!(f, "calling {} returned unexpected type {}", m, t) + } + SignalJniError::IntegerOverflow(m) => { + write!(f, "integer overflow during conversion of {}", m) + } + SignalJniError::UnexpectedPanic(e) => match e.downcast_ref::<&'static str>() { + Some(s) => write!(f, "unexpected panic: {}", s), + None => write!(f, "unknown unexpected panic"), + }, + } + } +} + +impl From for SignalJniError { + fn from(e: SignalProtocolError) -> SignalJniError { + SignalJniError::Signal(e) + } +} + +impl From for SignalJniError { + fn from(e: jni::errors::Error) -> SignalJniError { + SignalJniError::Jni(e) + } +} + +impl From for SignalProtocolError { + fn from(err: SignalJniError) -> SignalProtocolError { + match err { + SignalJniError::Signal(e) => e, + SignalJniError::Jni(e) => SignalProtocolError::FfiBindingError(e.to_string()), + SignalJniError::BadJniParameter(m) => { + SignalProtocolError::InvalidArgument(m.to_string()) + } + _ => SignalProtocolError::FfiBindingError(format!("{}", err)), + } + } +} + +pub fn throw_error(env: &JNIEnv, error: SignalJniError) { + let exception_type = match error { + SignalJniError::NullHandle => "java/lang/NullPointerException", + SignalJniError::UnexpectedPanic(_) => "java/lang/AssertionError", + SignalJniError::BadJniParameter(_) => "java/lang/AssertionError", + SignalJniError::UnexpectedJniResultType(_, _) => "java/lang/AssertionError", + SignalJniError::IntegerOverflow(_) => "java/lang/RuntimeException", + + SignalJniError::ExceptionDuringCallback(_) => "java/lang/RuntimeException", + + SignalJniError::Signal(SignalProtocolError::DuplicatedMessage(_, _)) => { + "org/whispersystems/libsignal/DuplicateMessageException" + } + + SignalJniError::Signal(SignalProtocolError::InvalidPreKeyId) + | SignalJniError::Signal(SignalProtocolError::InvalidSignedPreKeyId) + | SignalJniError::Signal(SignalProtocolError::InvalidSenderKeyId) => { + "org/whispersystems/libsignal/InvalidKeyIdException" + } + + SignalJniError::Signal(SignalProtocolError::NoKeyTypeIdentifier) + | SignalJniError::Signal(SignalProtocolError::SignatureValidationFailed) + | SignalJniError::Signal(SignalProtocolError::BadKeyType(_)) + | SignalJniError::Signal(SignalProtocolError::BadKeyLength(_, _)) => { + "org/whispersystems/libsignal/InvalidKeyException" + } + + SignalJniError::Signal(SignalProtocolError::SessionNotFound) => { + "org/whispersystems/libsignal/NoSessionException" + } + + SignalJniError::Signal(SignalProtocolError::InvalidMessage(_)) + | SignalJniError::Signal(SignalProtocolError::CiphertextMessageTooShort(_)) + | SignalJniError::Signal(SignalProtocolError::UnrecognizedCiphertextVersion(_)) + | SignalJniError::Signal(SignalProtocolError::UnrecognizedMessageVersion(_)) + | SignalJniError::Signal(SignalProtocolError::InvalidCiphertext) + | SignalJniError::Signal(SignalProtocolError::InvalidProtobufEncoding) => { + "org/whispersystems/libsignal/InvalidMessageException" + } + + SignalJniError::Signal(SignalProtocolError::LegacyCiphertextVersion(_)) => { + "org/whispersystems/libsignal/LegacyMessageException" + } + + SignalJniError::Signal(SignalProtocolError::UntrustedIdentity(_)) => { + "org/whispersystems/libsignal/UntrustedIdentityException" + } + + SignalJniError::Signal(SignalProtocolError::InvalidState(_, _)) + | SignalJniError::Signal(SignalProtocolError::NoSenderKeyState) + | SignalJniError::Signal(SignalProtocolError::InvalidSessionStructure) => { + "java/lang/IllegalStateException" + } + + SignalJniError::Signal(SignalProtocolError::InvalidArgument(_)) => { + "java/lang/IllegalArgumentException" + } + + SignalJniError::Signal(_) => "java/lang/RuntimeException", + + SignalJniError::Jni(_) => "java/lang/RuntimeException", + }; + + let error_string = match error { + SignalJniError::Signal(SignalProtocolError::UntrustedIdentity(addr)) => { + addr.name().to_string() + } + e => format!("{}", e), + }; + + let _ = env.throw_new(exception_type, error_string); +} + +pub type ObjectHandle = jlong; + +pub unsafe fn native_handle_cast( + handle: ObjectHandle, +) -> Result<&'static mut T, SignalJniError> { + /* + Should we try testing the encoded pointer for sanity here, beyond + being null? For example verifying that lowest bits are zero, + highest bits are zero, greater than 64K, etc? + */ + if handle == 0 { + return Err(SignalJniError::NullHandle); + } + + Ok(&mut *(handle as *mut T)) +} + +pub unsafe fn native_handle_cast_optional( + handle: ObjectHandle, +) -> Result, SignalJniError> { + if handle == 0 { + return Ok(None); + } + + Ok(Some(&mut *(handle as *mut T))) +} + +// A dummy value to return when we are throwing an exception +pub trait JniDummyValue { + fn dummy_value() -> Self; +} + +impl JniDummyValue for ObjectHandle { + fn dummy_value() -> Self { + 0 + } +} + +impl JniDummyValue for jint { + fn dummy_value() -> Self { + 0 as jint + } +} + +impl JniDummyValue for *mut _jobject { + fn dummy_value() -> Self { + 0 as jstring + } +} + +impl JniDummyValue for jboolean { + fn dummy_value() -> Self { + 0 as jboolean + } +} + +impl JniDummyValue for () { + fn dummy_value() -> Self {} +} + +pub fn run_ffi_safe Result + std::panic::UnwindSafe, R>( + env: &JNIEnv, + f: F, +) -> R +where + R: JniDummyValue, +{ + match std::panic::catch_unwind(f) { + Ok(Ok(r)) => r, + Ok(Err(e)) => { + throw_error(env, e); + R::dummy_value() + } + Err(r) => { + throw_error(env, SignalJniError::UnexpectedPanic(r)); + R::dummy_value() + } + } +} + +pub fn box_object(t: Result) -> Result { + match t { + Ok(t) => Ok(Box::into_raw(Box::new(t)) as ObjectHandle), + Err(e) => Err(SignalJniError::Signal(e)), + } +} + +pub fn to_jbytearray>( + env: &JNIEnv, + data: Result, +) -> Result { + let data = data?; + let data: &[u8] = data.as_ref(); + let out = env.new_byte_array(data.len() as i32)?; + let buf: Vec = data.iter().map(|i| *i as i8).collect(); + env.set_byte_array_region(out, 0, buf.as_slice())?; + Ok(out) +} + +pub fn jint_to_u32(v: jint) -> Result { + if v < 0 { + return Err(SignalJniError::IntegerOverflow(format!("{} to u32", v))); + } + Ok(v as u32) +} + +pub fn jint_to_u8(v: jint) -> Result { + if v < 0 || v > 255 { + return Err(SignalJniError::IntegerOverflow(format!("{} to u8", v))); + } + Ok(v as u8) +} + +pub fn jint_from_u32(value: Result) -> Result { + match value { + Ok(value) => { + let result = value as jint; + if result as u32 != value { + return Err(SignalJniError::IntegerOverflow(format!( + "{} to jint", + value + ))); + } + Ok(result) + } + Err(e) => Err(SignalJniError::Signal(e)), + } +} + +pub fn jlong_from_u64(value: Result) -> Result { + match value { + Ok(value) => { + let result = value as jlong; + if result as u64 != value { + return Err(SignalJniError::IntegerOverflow(format!( + "{} to jlong", + value + ))); + } + Ok(result) + } + Err(e) => Err(SignalJniError::Signal(e)), + } +} + +pub fn exception_check(env: &JNIEnv, fn_name: &'static str) -> Result<(), SignalJniError> { + fn exception_class_name(env: &JNIEnv, exn: JThrowable) -> Result { + let class_type = env.call_method(exn, "getClass", "()Ljava/lang/Class;", &[])?; + if let JValue::Object(class_type) = class_type { + let class_name = + env.call_method(class_type, "getCanonicalName", "()Ljava/lang/String;", &[])?; + + if let JValue::Object(class_name) = class_name { + let class_name: String = env.get_string(JString::from(class_name))?.into(); + Ok(class_name) + } else { + Err(SignalJniError::UnexpectedJniResultType( + "getCanonicalName", + class_name.type_name(), + )) + } + } else { + Err(SignalJniError::UnexpectedJniResultType( + "getClass", + class_type.type_name(), + )) + } + } + + if env.exception_check()? { + let throwable = env.exception_occurred()?; + env.exception_clear()?; + + let getmessage_sig = "()Ljava/lang/String;"; + + let exn_type = exception_class_name(env, throwable).ok(); + + if let Ok(jmessage) = env.call_method(throwable, "getMessage", getmessage_sig, &[]) { + if let JValue::Object(o) = jmessage { + let message: String = env.get_string(JString::from(o))?.into(); + return Err(SignalJniError::Signal( + SignalProtocolError::ApplicationCallbackThrewException( + fn_name, exn_type, message, + ), + )); + } + } + + return Err(SignalJniError::Signal( + SignalProtocolError::ApplicationCallbackThrewException( + fn_name, + exn_type, + "".to_string(), + ), + )); + } + + Ok(()) +} + +pub fn check_jobject_type( + env: &JNIEnv, + obj: jobject, + class_name: &'static str, +) -> Result { + if obj.is_null() { + return Err(SignalJniError::NullHandle); + } + + let class = env.find_class(class_name)?; + + if !env.is_instance_of(obj, class)? { + return Err(SignalJniError::BadJniParameter(class_name)); + } + + Ok(obj) +} + +pub fn get_object_with_native_handle( + env: &JNIEnv, + store_obj: jobject, + callback_args: &[JValue], + callback_sig: &'static str, + callback_fn: &'static str, +) -> Result, SignalJniError> { + let rvalue = env.call_method(store_obj, callback_fn, callback_sig, &callback_args)?; + exception_check(env, callback_fn)?; + + let obj = match rvalue { + JValue::Object(o) => *o, + _ => { + return Err(SignalJniError::UnexpectedJniResultType( + callback_fn, + rvalue.type_name(), + )) + } + }; + + if obj.is_null() { + return Ok(None); + } + + let handle = env.call_method(obj, "nativeHandle", "()J", &[])?; + exception_check(env, "nativeHandle")?; + match handle { + JValue::Long(handle) => { + let object = unsafe { native_handle_cast::(handle)? }; + Ok(Some(object.clone())) + } + _ => Err(SignalJniError::UnexpectedJniResultType( + "nativeHandle", + handle.type_name(), + )), + } +} + +pub fn get_object_with_serialization( + env: &JNIEnv, + store_obj: jobject, + callback_args: &[JValue], + callback_sig: &'static str, + callback_fn: &'static str, +) -> Result>, SignalJniError> { + let rvalue = env.call_method(store_obj, callback_fn, callback_sig, &callback_args)?; + exception_check(env, callback_fn)?; + + let obj = match rvalue { + JValue::Object(o) => *o, + _ => { + return Err(SignalJniError::UnexpectedJniResultType( + callback_fn, + rvalue.type_name(), + )) + } + }; + + if obj.is_null() { + return Ok(None); + } + + let bytes = env.call_method(obj, "serialize", "()[B", &[])?; + exception_check(env, "serialize")?; + + match bytes { + JValue::Object(o) => Ok(Some(env.convert_byte_array(*o)?)), + _ => Err(SignalJniError::UnexpectedJniResultType( + "serialize", + bytes.type_name(), + )), + } +} + +pub fn jobject_from_serialized<'a>( + env: &'a JNIEnv, + class_name: &str, + serialized: &[u8], +) -> Result, SignalJniError> { + let class_type = env.find_class(class_name)?; + let ctor_sig = "([B)V"; + let ctor_args = [JValue::from(to_jbytearray(env, Ok(serialized))?)]; + Ok(env.new_object(class_type, ctor_sig, &ctor_args)?) +} + +pub fn jobject_from_native_handle<'a>( + env: &'a JNIEnv, + class_name: &str, + boxed_handle: ObjectHandle, +) -> Result, SignalJniError> { + let class_type = env.find_class(class_name)?; + let ctor_sig = "(J)V"; + let ctor_args = [JValue::from(boxed_handle)]; + Ok(env.new_object(class_type, ctor_sig, &ctor_args)?) +} + +#[macro_export] +macro_rules! jni_fn_deserialize { + ( $nm:ident is $func:path ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + data: jbyteArray, + ) -> ObjectHandle { + run_ffi_safe(&env, || { + let data = env.convert_byte_array(data)?; + box_object($func(data.as_ref())) + }) + } + }; +} + +#[macro_export] +macro_rules! jni_fn_get_new_boxed_obj { + ( $nm:ident($rt:ty) from $typ:ty, $body:expr ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> ObjectHandle { + run_ffi_safe(&env, || { + let obj = native_handle_cast::<$typ>(handle)?; + return box_object::<$rt>($body(obj)); + }) + } + }; +} + +#[macro_export] +macro_rules! jni_fn_get_new_boxed_optional_obj { + ( $nm:ident($rt:ty) from $typ:ty, $body:expr ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> ObjectHandle { + run_ffi_safe(&env, || { + let obj = native_handle_cast::<$typ>(handle)?; + let result: Option<$rt> = $body(obj)?; + if let Some(result) = result { + box_object::<$rt>(Ok(result)) + } else { + Ok(0 as ObjectHandle) + } + }) + } + }; +} + +#[macro_export] +macro_rules! jni_fn_get_jint { + ( $nm:ident($typ:ty) using $body:expr ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> jint { + run_ffi_safe(&env, || { + let obj = native_handle_cast::<$typ>(handle)?; + return jint_from_u32($body(obj)); + }) + } + }; +} + +#[macro_export] +macro_rules! jni_fn_get_jboolean { + ( $nm:ident($typ:ty) using $body:expr ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> jboolean { + run_ffi_safe(&env, || { + let obj = native_handle_cast::<$typ>(handle)?; + let r: bool = $body(obj)?; + Ok(r as jboolean) + }) + } + }; +} + +/* +Without the indirection of inner_get, rust can't deduce the Error type +if the provided lambda just returns Ok(something) +*/ +#[macro_export] +macro_rules! jni_fn_get_jstring { + ( $nm:ident($typ:ty) using $body:expr ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> jstring { + fn inner_get(t: &$typ) -> Result { + $body(&t) + } + run_ffi_safe(&env, || { + let obj: &mut $typ = native_handle_cast::<$typ>(handle)?; + return Ok(env.new_string(inner_get(&obj)?)?.into_inner()); + }) + } + }; + ( $nm:ident($typ:ty) using $func:path ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> jstring { + run_ffi_safe(&env, || { + let obj: &mut $typ = native_handle_cast::<$typ>(handle)?; + return Ok(env.new_string($func(&obj)?)?.into_inner()); + }) + } + }; +} + +#[macro_export] +macro_rules! jni_fn_get_jbytearray { + ( $nm:ident($typ:ty) using $body:expr ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm( + env: JNIEnv, + _class: JClass, + handle: ObjectHandle, + ) -> jbyteArray { + run_ffi_safe(&env, || { + let obj = native_handle_cast::<$typ>(handle)?; + return to_jbytearray(&env, $body(obj)); + }) + } + }; +} + +#[macro_export] +macro_rules! jni_fn_destroy { + ( $nm:ident destroys $typ:ty ) => { + #[no_mangle] + pub unsafe extern "system" fn $nm(_env: JNIEnv, _class: JClass, handle: ObjectHandle) { + if handle != 0 { + let _boxed_value = Box::from_raw(handle as *mut $typ); + } + } + }; +}