jni_bridge.rs

10.3 KB · raw

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}