-
-
Notifications
You must be signed in to change notification settings - Fork 421
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
Conversation
Makes the diff against libsignal-protocol-java easier to read
Initial commit
Add SenderKeyStore handling
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
Add CI using Github actions
Add ECPublicKey comparison
(Namely the exception types and the callback which failed)
Update with Context (unused) and capture more debug info
Maybe we want this in |
There was a problem hiding this 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)
rust/jni/src/util.rs
Outdated
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())?; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rust/jni/src/util.rs
Outdated
let message: String = env.get_string(JString::from(o))?.into(); | ||
return Err(SignalJniError::Signal( | ||
SignalProtocolError::ApplicationCallbackThrewException( | ||
fn_name, exn_type, message, |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
rust/jni/src/util.rs
Outdated
|
||
pub fn jint_to_u32(v: jint) -> Result<u32, SignalJniError> { | ||
if v < 0 { | ||
return Err(SignalJniError::IntegerOverflow(format!("{} to u32", v))); |
There was a problem hiding this comment.
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 int
s or long
s? If they're opaque numeric identifiers it'd be better to reinterpret them than fail.
rust/jni/src/lib.rs
Outdated
) -> ObjectHandle { | ||
run_ffi_safe(&env, || { | ||
let offset = jint_to_u32(offset)? as usize; | ||
let data = env.convert_byte_array(data)?; |
There was a problem hiding this comment.
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
.
rust/jni/src/lib.rs
Outdated
_class: JClass, | ||
) -> ObjectHandle { | ||
run_ffi_safe(&env, || { | ||
let mut rng = rand::rngs::OsRng; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
rust/jni/src/lib.rs
Outdated
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>( |
There was a problem hiding this comment.
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?
rust/jni/src/lib.rs
Outdated
]; | ||
|
||
let sender_key_name_ctor_sig = "(Ljava/lang/String;Ljava/lang/String;I)V"; | ||
let sender_key_name_jobject = env.new_object( |
There was a problem hiding this comment.
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?)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
rust/jni/src/lib.rs
Outdated
"org/whispersystems/libsignal/IdentityKey", | ||
identity.serialize().as_ref(), | ||
)?; | ||
let callback_sig = "(Lorg/whispersystems/libsignal/SignalProtocolAddress;Lorg/whispersystems/libsignal/IdentityKey;)Z"; |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rust/jni/src/lib.rs
Outdated
} | ||
} | ||
|
||
fn create_identity_store<'a>( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
rust/jni/src/lib.rs
Outdated
}) | ||
} | ||
|
||
// The following are just exposed to make it possible to retain some of the Java tests: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Use 'bool' for several more Boolean results
5c36459
to
3d24f94
Compare
Moved to |
…leases-url Option to define github account for dowloading release artifacts
755cc05 is current HEAD of libsignal-jni