Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge libsignal-jni #3

Merged
merged 28 commits into from
Oct 19, 2020
Merged

Merge libsignal-jni #3

merged 28 commits into from
Oct 19, 2020

Conversation

jack-signal
Copy link
Contributor

755cc05 is current HEAD of libsignal-jni

Makes the diff against libsignal-protocol-java easier to read
Also fix isTrustedIdentity which was still just a stub

Fix some bugs around error reporting and choice of exception
Add storage layer interactions, session encryption/decryption
The current uppercase leading char is unusual for Java, and having a `native`
prefix makes it more clear what is happening on the Java side.
Add 'native' prefix to all JNI functions
Allows simplifying some of the Java-side logic
Add offset argument for PublicKey deserialization
Cannot build due to needing to reference the private libsignal-protocol-rust
(Namely the exception types and the callback which failed)
Update with Context (unused) and capture more debug info
@jack-signal
Copy link
Contributor Author

Maybe we want this in rust/ffi/jni instead? IDK

Copy link
Contributor

@jrose-signal jrose-signal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several questions and comments but of course this code is already being tested from its original repo.

(also discussing the directory layout question elsewhere, don't think it has to block this)

let data: &[u8] = data.as_ref();
let out = env.new_byte_array(data.len() as i32)?;
let buf: Vec<i8> = data.iter().map(|i| *i as i8).collect();
env.set_byte_array_region(out, 0, buf.as_slice())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we can use let out = env.byte_array_from_slice(data.as_ref())?; for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#9

let message: String = env.get_string(JString::from(o))?.into();
return Err(SignalJniError::Signal(
SignalProtocolError::ApplicationCallbackThrewException(
fn_name, exn_type, message,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking) Can we keep around the entire exception here instead of just the name and message? (cf. the "cause" field in java.lang.Exception).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that would definitely make debugging simpler. #5


pub fn jint_to_u32(v: jint) -> Result<u32, SignalJniError> {
if v < 0 {
return Err(SignalJniError::IntegerOverflow(format!("{} to u32", v)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure we don't use all bits of any ints or longs? If they're opaque numeric identifiers it'd be better to reinterpret them than fail.

) -> ObjectHandle {
run_ffi_safe(&env, || {
let offset = jint_to_u32(offset)? as usize;
let data = env.convert_byte_array(data)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking) I feel like there ought to be a zero-copy version of this as well (cf. get_auto_byte_array_elements) but I see it's non-trivial, what with the mismatched signedness between u8 and jbyte.

_class: JClass,
) -> ObjectHandle {
run_ffi_safe(&env, || {
let mut rng = rand::rngs::OsRng;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in libsignal-ffi as well, but why OsRng over ThreadRng? They're both listed as cryptographically secure, and OsRng will generally be slower since it's calling out to the OS every time instead of just periodically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThreadRng is probably fine. It needed to be avoided until recently because before rand 0.7 it used HC-128 which is vulnerable to side channel attacks (https://cr.yp.to/streamciphers/leaks.html). However it appears that rand 0.7 has switched to ChaCha20 which should be fine. #6

We should probably have a way of toggling this across all crates.

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>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking) Maybe worth breaking the stores and the supporting logic for them out into another file?

];

let sender_key_name_ctor_sig = "(Ljava/lang/String;Ljava/lang/String;I)V";
let sender_key_name_jobject = env.new_object(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make SenderKeyName et al self-contained Java objects instead of wrappers around native handles like the others? (Does this predate adding clone() to all the types, so they can be escaped more easily?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SenderKeyName is a wrapper around a handle like the others. But the Java callback is expecting to be passed not a handle but an actual Java object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, is there no way to make an object from the handle you already have, rather than asking it to make a new one? I guess that would be a more intrusive change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For most types in Java I added a constructor which takes a long (aka ObjectHandle) which takes ownership of said handle. I'm not sure why I didn't do that here, probably just an oversight.

"org/whispersystems/libsignal/IdentityKey",
identity.serialize().as_ref(),
)?;
let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;Lorg/whispersystems/libsignal/IdentityKey;)Z";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking) Probably worth making a macro to build these signatures (later).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely agree. Unlike the function names, this probably doesn't even require a proc macro.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#10

}
}

fn create_identity_store<'a>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems redundant with the new method, but is it a convention to do error checking before that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think this is just an artifact of how the code evolved. It would be better to move these checks to new. #7

})
}

// The following are just exposed to make it possible to retain some of the Java tests:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could actually be useful for the iOS client too, but the alternative would just be shoving this logic up into the Java layer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC the reason not to do so was because nothing outside of tests, and the core protocol library itself, need to know about the {Alice,Bob}SignalProtocolParameters types. Really the tests could have just been removed from the Java side but I wanted to minimize the diff there to make review simpler. I would rather we have higher level tests (that verify actual behavior as expected by the clients) rather than what are effectively unit tests of protocol level details.

jrose-signal added a commit that referenced this pull request Oct 16, 2020
Use 'bool' for several more Boolean results
jrose-signal added a commit that referenced this pull request Oct 16, 2020
@jack-signal
Copy link
Contributor Author

jack-signal commented Oct 16, 2020

Moved to rust/bridge/jni to match ffi. I believe all comments are captured in tickets now so I think this is good to merge and we can proceed from there.

@jack-signal jack-signal merged commit c0b6076 into master Oct 19, 2020
@jack-signal jack-signal deleted the jack/merge-jni branch October 19, 2020 20:53
johanvos pushed a commit to johanvos/libsignal-client that referenced this pull request May 16, 2023
…leases-url

Option to define github account for dowloading release artifacts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants