1use super::CoreNoiseSession;2use jni::{3 JNIEnv,4 objects::{JByteArray, JObject},5 sys::{jbyteArray, jlong},6};7use once_cell::sync::Lazy;8use std::{9 collections::HashMap,10 ptr,11 sync::{12 Mutex,13 atomic::{AtomicI64, Ordering},14 },15};16use zeroize::Zeroizing;1718static NEXT_HANDLE: AtomicI64 = AtomicI64::new(1);19static SESSIONS: Lazy<Mutex<HashMap<i64, CoreNoiseSession>>> =20 Lazy::new(|| Mutex::new(HashMap::new()));2122type NativeResult<T> = Result<T, String>;2324fn bytes(env: &JNIEnv<'_>, value: JByteArray<'_>) -> NativeResult<Zeroizing<Vec<u8>>> {25 env.convert_byte_array(value)26 .map(Zeroizing::new)27 .map_err(|error| error.to_string())28}2930#[cfg(test)]31mod tests {32 use super::*;3334 #[test]35 fn jni_byte_copies_use_zeroizing_storage() {36 let _: for<'a> fn(37 &JNIEnv<'a>,38 JByteArray<'a>,39 ) -> NativeResult<zeroize::Zeroizing<Vec<u8>>> = bytes;40 }41}4243fn create(session: CoreNoiseSession) -> NativeResult<jlong> {44 let handle = NEXT_HANDLE.fetch_add(1, Ordering::Relaxed);45 SESSIONS46 .lock()47 .map_err(|_| "Noise session registry is unavailable".to_string())?48 .insert(handle, session);49 Ok(handle)50}5152fn with_session<T>(53 handle: jlong,54 operation: impl FnOnce(&mut CoreNoiseSession) -> NativeResult<T>,55) -> NativeResult<T> {56 let mut sessions = SESSIONS57 .lock()58 .map_err(|_| "Noise session registry is unavailable".to_string())?;59 let session = sessions60 .get_mut(&handle)61 .ok_or_else(|| "Noise session handle is invalid or closed".to_string())?;62 operation(session)63}6465fn throw(env: &mut JNIEnv<'_>, message: String) {66 let _ = env.throw_new("java/lang/IllegalStateException", message);67}6869fn long_result(env: &mut JNIEnv<'_>, result: NativeResult<jlong>) -> jlong {70 match result {71 Ok(value) => value,72 Err(error) => {73 throw(env, error);74 075 }76 }77}7879fn array_result(env: &mut JNIEnv<'_>, result: NativeResult<Vec<u8>>) -> jbyteArray {80 match result {81 Ok(value) => match env.byte_array_from_slice(&value) {82 Ok(array) => array.into_raw(),83 Err(error) => {84 throw(env, error.to_string());85 ptr::null_mut()86 }87 },88 Err(error) => {89 throw(env, error);90 ptr::null_mut()91 }92 }93}9495#[unsafe(no_mangle)]96pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateNkInitiator(97 mut env: JNIEnv,98 _object: JObject,99 remote_public_key: JByteArray,100) -> jlong {101 let result = bytes(&env, remote_public_key)102 .and_then(|key| CoreNoiseSession::nk_initiator(&key).and_then(create));103 long_result(&mut env, result)104}105106#[unsafe(no_mangle)]107pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateNkResponder(108 mut env: JNIEnv,109 _object: JObject,110 local_private_key: JByteArray,111 local_public_key: JByteArray,112) -> jlong {113 let result = bytes(&env, local_private_key).and_then(|private| {114 bytes(&env, local_public_key)115 .and_then(|public| CoreNoiseSession::nk_responder(&private, &public).and_then(create))116 });117 long_result(&mut env, result)118}119120#[cfg(feature = "test-constructors")]121#[unsafe(no_mangle)]122pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateNkInitiatorForTest(123 mut env: JNIEnv,124 _object: JObject,125 remote_public_key: JByteArray,126 ephemeral_private_key: JByteArray,127) -> jlong {128 let result = bytes(&env, remote_public_key).and_then(|remote| {129 bytes(&env, ephemeral_private_key).and_then(|ephemeral| {130 CoreNoiseSession::nk_initiator_with_ephemeral_for_test(&remote, &ephemeral)131 .and_then(create)132 })133 });134 long_result(&mut env, result)135}136137#[cfg(feature = "test-constructors")]138#[unsafe(no_mangle)]139pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateNkResponderForTest(140 mut env: JNIEnv,141 _object: JObject,142 local_private_key: JByteArray,143 local_public_key: JByteArray,144 ephemeral_private_key: JByteArray,145) -> jlong {146 let result = bytes(&env, local_private_key).and_then(|private| {147 bytes(&env, local_public_key).and_then(|public| {148 bytes(&env, ephemeral_private_key).and_then(|ephemeral| {149 CoreNoiseSession::nk_responder_with_ephemeral_for_test(150 &private, &public, &ephemeral,151 )152 .and_then(create)153 })154 })155 });156 long_result(&mut env, result)157}158159#[unsafe(no_mangle)]160pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateKkInitiator(161 mut env: JNIEnv,162 _object: JObject,163 local_private_key: JByteArray,164 local_public_key: JByteArray,165 remote_public_key: JByteArray,166) -> jlong {167 let result = bytes(&env, local_private_key).and_then(|private| {168 bytes(&env, local_public_key).and_then(|public| {169 bytes(&env, remote_public_key).and_then(|remote| {170 CoreNoiseSession::kk_initiator(&private, &public, &remote).and_then(create)171 })172 })173 });174 long_result(&mut env, result)175}176177#[unsafe(no_mangle)]178pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateKkResponder(179 mut env: JNIEnv,180 _object: JObject,181 local_private_key: JByteArray,182 local_public_key: JByteArray,183 remote_public_key: JByteArray,184) -> jlong {185 let result = bytes(&env, local_private_key).and_then(|private| {186 bytes(&env, local_public_key).and_then(|public| {187 bytes(&env, remote_public_key).and_then(|remote| {188 CoreNoiseSession::kk_responder(&private, &public, &remote).and_then(create)189 })190 })191 });192 long_result(&mut env, result)193}194195#[cfg(feature = "test-constructors")]196#[unsafe(no_mangle)]197pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateKkInitiatorForTest(198 mut env: JNIEnv,199 _object: JObject,200 local_private_key: JByteArray,201 local_public_key: JByteArray,202 remote_public_key: JByteArray,203 ephemeral_private_key: JByteArray,204) -> jlong {205 let result = bytes(&env, local_private_key).and_then(|private| {206 bytes(&env, local_public_key).and_then(|public| {207 bytes(&env, remote_public_key).and_then(|remote| {208 bytes(&env, ephemeral_private_key).and_then(|ephemeral| {209 CoreNoiseSession::kk_initiator_with_ephemeral_for_test(210 &private, &public, &remote, &ephemeral,211 )212 .and_then(create)213 })214 })215 })216 });217 long_result(&mut env, result)218}219220#[cfg(feature = "test-constructors")]221#[unsafe(no_mangle)]222pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeCreateKkResponderForTest(223 mut env: JNIEnv,224 _object: JObject,225 local_private_key: JByteArray,226 local_public_key: JByteArray,227 remote_public_key: JByteArray,228 ephemeral_private_key: JByteArray,229) -> jlong {230 let result = bytes(&env, local_private_key).and_then(|private| {231 bytes(&env, local_public_key).and_then(|public| {232 bytes(&env, remote_public_key).and_then(|remote| {233 bytes(&env, ephemeral_private_key).and_then(|ephemeral| {234 CoreNoiseSession::kk_responder_with_ephemeral_for_test(235 &private, &public, &remote, &ephemeral,236 )237 .and_then(create)238 })239 })240 })241 });242 long_result(&mut env, result)243}244245#[unsafe(no_mangle)]246pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeWriteHandshake(247 mut env: JNIEnv,248 _object: JObject,249 handle: jlong,250 payload: JByteArray,251) -> jbyteArray {252 let result = bytes(&env, payload)253 .and_then(|value| with_session(handle, |session| session.write_handshake(&value)));254 array_result(&mut env, result)255}256257#[unsafe(no_mangle)]258pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeReadHandshake(259 mut env: JNIEnv,260 _object: JObject,261 handle: jlong,262 message: JByteArray,263) -> jbyteArray {264 let result = bytes(&env, message)265 .and_then(|value| with_session(handle, |session| session.read_handshake(&value)));266 array_result(&mut env, result)267}268269#[unsafe(no_mangle)]270pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeEncrypt(271 mut env: JNIEnv,272 _object: JObject,273 handle: jlong,274 plaintext: JByteArray,275) -> jbyteArray {276 let result = bytes(&env, plaintext)277 .and_then(|value| with_session(handle, |session| session.encrypt(&value)));278 array_result(&mut env, result)279}280281#[unsafe(no_mangle)]282pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeDecrypt(283 mut env: JNIEnv,284 _object: JObject,285 handle: jlong,286 ciphertext: JByteArray,287) -> jbyteArray {288 let result = bytes(&env, ciphertext)289 .and_then(|value| with_session(handle, |session| session.decrypt(&value)));290 array_result(&mut env, result)291}292293#[unsafe(no_mangle)]294pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeHandshakeHash(295 mut env: JNIEnv,296 _object: JObject,297 handle: jlong,298) -> jbyteArray {299 let result = with_session(handle, |session| session.handshake_hash());300 array_result(&mut env, result)301}302303#[unsafe(no_mangle)]304pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeRekeyOutgoing(305 mut env: JNIEnv,306 _object: JObject,307 handle: jlong,308) {309 if let Err(error) = with_session(handle, |session| session.rekey_outgoing()) {310 throw(&mut env, error);311 }312}313314#[unsafe(no_mangle)]315pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeRekeyIncoming(316 mut env: JNIEnv,317 _object: JObject,318 handle: jlong,319) {320 if let Err(error) = with_session(handle, |session| session.rekey_incoming()) {321 throw(&mut env, error);322 }323}324325#[unsafe(no_mangle)]326pub extern "system" fn Java_com_peachpasswords_sync_RustNoiseNative_nativeClose(327 mut env: JNIEnv,328 _object: JObject,329 handle: jlong,330) {331 match SESSIONS.lock() {332 Ok(mut sessions) => {333 sessions.remove(&handle);334 }335 Err(_) => throw(&mut env, "Noise session registry is unavailable".into()),336 }337}