diff --git a/rust/bridge/jni/bin/gen_java_decl.py b/rust/bridge/jni/bin/gen_java_decl.py index cdb55cf9..8106640b 100755 --- a/rust/bridge/jni/bin/gen_java_decl.py +++ b/rust/bridge/jni/bin/gen_java_decl.py @@ -39,7 +39,8 @@ ignore_this_warning = re.compile( "(" r"WARN: Can't find .*\. This usually means that this type was incompatible or not found\.|" r"WARN: Missing `\[defines\]` entry for `feature = \".*\"` in cbindgen config\.|" - r"WARN: Skip libsignal-bridge::_ - \(not `pub`\)\." + r"WARN: Skip libsignal-bridge::_ - \(not `pub`\)\.|" + r"WARN: Couldn't find path for Array\(Path\(GenericPath \{ .+ \}\), Name\(\"LEN\"\)\), skipping associated constants" ")") unknown_warning = False diff --git a/rust/bridge/node/bin/gen_ts_decl.py b/rust/bridge/node/bin/gen_ts_decl.py index 95c8ef37..04114bdf 100755 --- a/rust/bridge/node/bin/gen_ts_decl.py +++ b/rust/bridge/node/bin/gen_ts_decl.py @@ -39,6 +39,9 @@ def translate_to_ts(typ): if typ in type_map: return type_map[typ] + if typ.startswith('[u8;') or typ.startswith('&[u8;'): + return 'Buffer' + if typ.startswith('&mutdyn'): return typ[7:] diff --git a/rust/bridge/shared/src/ffi/convert.rs b/rust/bridge/shared/src/ffi/convert.rs index 94d5feb6..a81bad83 100644 --- a/rust/bridge/shared/src/ffi/convert.rs +++ b/rust/bridge/shared/src/ffi/convert.rs @@ -253,6 +253,21 @@ impl ResultTypeInfo for uuid::Uuid { } } +impl SimpleArgTypeInfo for &'_ [u8; LEN] { + type ArgType = *const [u8; LEN]; + #[allow(clippy::not_unsafe_ptr_arg_deref)] + fn convert_from(arg: *const [u8; LEN]) -> SignalFfiResult { + unsafe { arg.as_ref() }.ok_or(SignalFfiError::NullPointer) + } +} + +impl ResultTypeInfo for [u8; LEN] { + type ResultType = [u8; LEN]; + fn convert_into(self) -> SignalFfiResult { + Ok(self) + } +} + macro_rules! store { ($name:ident) => { paste! { @@ -519,6 +534,7 @@ macro_rules! ffi_arg_type { (Context) => (*mut libc::c_void); (Timestamp) => (u64); (Uuid) => (*const [u8; 16]); + (&[u8; $len:expr]) => (*const [u8; $len]); (&[& $typ:ty]) => (*const *const $typ); (&mut dyn $typ:ty) => (*const paste!(ffi::[])); (& $typ:ty) => (*const $typ); @@ -554,5 +570,6 @@ macro_rules! ffi_result_type { (Option<$typ:ty>) => (*mut $typ); (Timestamp) => (u64); (Uuid) => ([u8; 16]); + ([u8; $len:expr]) => ([u8; $len]); ( $typ:ty ) => (*mut $typ); } diff --git a/rust/bridge/shared/src/jni/convert.rs b/rust/bridge/shared/src/jni/convert.rs index f0ce908b..edbdb38c 100644 --- a/rust/bridge/shared/src/jni/convert.rs +++ b/rust/bridge/shared/src/jni/convert.rs @@ -552,6 +552,36 @@ impl ResultTypeInfo for Option> { } } +impl<'storage, 'context: 'storage, const LEN: usize> ArgTypeInfo<'storage, 'context> + for &'storage [u8; LEN] +{ + type ArgType = jbyteArray; + type StoredType = AutoArray<'context, 'context, jbyte>; + fn borrow(env: &'context JNIEnv, foreign: Self::ArgType) -> SignalJniResult { + Ok(env.get_byte_array_elements(foreign, ReleaseMode::NoCopyBack)?) + } + fn load_from( + _env: &JNIEnv, + stored: &'storage mut Self::StoredType, + ) -> SignalJniResult<&'storage [u8; LEN]> { + unsafe { std::slice::from_raw_parts(stored.as_ptr() as *const u8, stored.size()? as usize) } + .try_into() + .map_err(|_| SignalJniError::DeserializationFailed(std::any::type_name::<[u8; LEN]>())) + } +} + +impl ResultTypeInfo for [u8; LEN] { + type ResultType = jbyteArray; + fn convert_into(self, env: &JNIEnv) -> SignalJniResult { + self.as_ref().convert_into(env) + } + fn convert_into_jobject(signal_jni_result: &SignalJniResult) -> JObject { + signal_jni_result + .as_ref() + .map_or(JObject::null(), |&jobj| JObject::from(jobj)) + } +} + impl ResultTypeInfo for uuid::Uuid { type ResultType = jobject; fn convert_into(self, env: &JNIEnv) -> SignalJniResult { @@ -864,6 +894,9 @@ macro_rules! jni_arg_type { (&mut [u8]) => { jni::jbyteArray }; + (&[u8; $len:expr]) => { + jni::jbyteArray + }; (Context) => { jni::JObject }; @@ -953,6 +986,9 @@ macro_rules! jni_result_type { (Vec) => { jni::jbyteArray }; + ([u8; $len:expr]) => { + jni::jbyteArray + }; (Option<$typ:tt>) => { jni_result_type!($typ) }; diff --git a/rust/bridge/shared/src/jni/error.rs b/rust/bridge/shared/src/jni/error.rs index 121c7dc3..28f9c295 100644 --- a/rust/bridge/shared/src/jni/error.rs +++ b/rust/bridge/shared/src/jni/error.rs @@ -24,6 +24,7 @@ pub enum SignalJniError { SignalCrypto(SignalCryptoError), Jni(jni::errors::Error), BadJniParameter(&'static str), + DeserializationFailed(&'static str), UnexpectedJniResultType(&'static str, &'static str), NullHandle, IntegerOverflow(String), @@ -46,6 +47,9 @@ impl fmt::Display for SignalJniError { SignalJniError::IntegerOverflow(m) => { write!(f, "integer overflow during conversion of {}", m) } + SignalJniError::DeserializationFailed(ty) => { + write!(f, "failed to deserialize {}", ty) + } SignalJniError::HsmEnclave(e) => { write!(f, "{}", e) } diff --git a/rust/bridge/shared/src/jni/mod.rs b/rust/bridge/shared/src/jni/mod.rs index 497eaa15..06ee3986 100644 --- a/rust/bridge/shared/src/jni/mod.rs +++ b/rust/bridge/shared/src/jni/mod.rs @@ -220,9 +220,8 @@ fn throw_error(env: &JNIEnv, error: SignalJniError) { SignalJniError::Signal(SignalProtocolError::InvalidArgument(_)) | SignalJniError::SignalCrypto(SignalCryptoError::UnknownAlgorithm(_, _)) | SignalJniError::SignalCrypto(SignalCryptoError::InvalidInputSize) - | SignalJniError::SignalCrypto(SignalCryptoError::InvalidNonceSize) => { - "java/lang/IllegalArgumentException" - } + | SignalJniError::SignalCrypto(SignalCryptoError::InvalidNonceSize) + | SignalJniError::DeserializationFailed(_) => "java/lang/IllegalArgumentException", SignalJniError::IntegerOverflow(_) | SignalJniError::Jni(_) diff --git a/rust/bridge/shared/src/node/convert.rs b/rust/bridge/shared/src/node/convert.rs index f90479f1..4bc31712 100644 --- a/rust/bridge/shared/src/node/convert.rs +++ b/rust/bridge/shared/src/node/convert.rs @@ -608,6 +608,39 @@ impl<'a> ResultTypeInfo<'a> for Vec { } } +/// Loads from a JsBuffer, assuming it won't be mutated while in use. +/// See [`AssumedImmutableBuffer`]. +impl<'storage, 'context: 'storage, const LEN: usize> ArgTypeInfo<'storage, 'context> + for &'storage [u8; LEN] +{ + type ArgType = JsBuffer; + type StoredType = AssumedImmutableBuffer<'context>; + fn borrow( + cx: &mut FunctionContext, + foreign: Handle<'context, Self::ArgType>, + ) -> NeonResult { + let result = AssumedImmutableBuffer::new(cx, foreign); + if result.buffer.len() != LEN { + cx.throw_error(format!( + "buffer has incorrect length {} (expected {})", + result.buffer.len(), + LEN + ))?; + } + Ok(result) + } + fn load_from(stored: &'storage mut Self::StoredType) -> Self { + stored.buffer.try_into().expect("checked length already") + } +} + +impl<'a, const LEN: usize> ResultTypeInfo<'a> for [u8; LEN] { + type ResultType = JsBuffer; + fn convert_into(self, cx: &mut impl Context<'a>) -> NeonResult> { + self.as_ref().convert_into(cx) + } +} + impl<'a, T: ResultTypeInfo<'a>> ResultTypeInfo<'a> for NeonResult { type ResultType = T::ResultType; fn convert_into(self, cx: &mut impl Context<'a>) -> NeonResult> {