Skip to content

Commit

Permalink
Add libsignal-jni-testing crate with test-only functions for Java
Browse files Browse the repository at this point in the history
This parallels the exiting libsignal-jni crate but exports functions from 
libsignal-bridge-testing instead of libsignal-bridge. The crate is compiled as 
a separate shared object that is included in the published libsignal package, 
but which can be excluded at Android packaging time.
  • Loading branch information
akonradi-signal authored Jul 9, 2024
1 parent c6857dd commit 0e4d420
Show file tree
Hide file tree
Showing 24 changed files with 371 additions and 51 deletions.
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"rust/zkgroup",
"rust/bridge/ffi",
"rust/bridge/jni",
"rust/bridge/jni/testing",
"rust/bridge/node",
]
default-members = [
Expand Down
4 changes: 3 additions & 1 deletion acknowledgments/acknowledgments.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ <h1>Third Party Licenses</h1>
<h2>Overview of licenses:</h2>
<ul class="licenses-overview">
<li><a href="#MIT">MIT License</a> (315)</li>
<li><a href="#AGPL-3.0">GNU Affero General Public License v3.0</a> (25)</li>
<li><a href="#AGPL-3.0">GNU Affero General Public License v3.0</a> (27)</li>
<li><a href="#Apache-2.0">Apache License 2.0</a> (12)</li>
<li><a href="#BSD-3-Clause">BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License</a> (8)</li>
<li><a href="#ISC">ISC License</a> (6)</li>
Expand Down Expand Up @@ -736,11 +736,13 @@ <h4>Used by:</h4>
<li><a href="https://crates.io/crates/attest">attest 0.1.0</a></li>
<li><a href="https://crates.io/crates/libsignal-ffi">libsignal-ffi 0.52.1</a></li>
<li><a href="https://crates.io/crates/libsignal-jni">libsignal-jni 0.52.1</a></li>
<li><a href="https://crates.io/crates/libsignal-jni-testing">libsignal-jni-testing 0.52.1</a></li>
<li><a href="https://crates.io/crates/libsignal-node">libsignal-node 0.52.1</a></li>
<li><a href="https://crates.io/crates/signal-neon-futures">signal-neon-futures 0.1.0</a></li>
<li><a href="https://crates.io/crates/signal-neon-futures-tests">signal-neon-futures-tests 0.1.0</a></li>
<li><a href="https://crates.io/crates/libsignal-bridge">libsignal-bridge 0.1.0</a></li>
<li><a href="https://crates.io/crates/libsignal-bridge-macros">libsignal-bridge-macros 0.1.0</a></li>
<li><a href="https://crates.io/crates/libsignal-bridge-testing">libsignal-bridge-testing 0.1.0</a></li>
<li><a href="https://crates.io/crates/libsignal-bridge-types">libsignal-bridge-types 0.1.0</a></li>
<li><a href="https://crates.io/crates/libsignal-core">libsignal-core 0.1.0</a></li>
<li><a href="https://crates.io/crates/signal-crypto">signal-crypto 0.1.0</a></li>
Expand Down
2 changes: 1 addition & 1 deletion acknowledgments/acknowledgments.md
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
```

## attest 0.1.0, libsignal-ffi 0.52.1, libsignal-jni 0.52.1, libsignal-node 0.52.1, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-bridge-types 0.1.0, libsignal-core 0.1.0, signal-crypto 0.1.0, device-transfer 0.1.0, signal-media 0.1.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, signal-pin 0.1.0, poksho 0.7.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0
## attest 0.1.0, libsignal-ffi 0.52.1, libsignal-jni 0.52.1, libsignal-jni-testing 0.52.1, libsignal-node 0.52.1, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-bridge-testing 0.1.0, libsignal-bridge-types 0.1.0, libsignal-core 0.1.0, signal-crypto 0.1.0, device-transfer 0.1.0, signal-media 0.1.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, signal-pin 0.1.0, poksho 0.7.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0

```
GNU AFFERO GENERAL PUBLIC LICENSE
Expand Down
2 changes: 1 addition & 1 deletion acknowledgments/acknowledgments.plist
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ You should also get your employer (if you work as a programmer) or school, if an
<key>License</key>
<string>GNU Affero General Public License v3.0</string>
<key>Title</key>
<string>attest 0.1.0, libsignal-ffi 0.52.1, libsignal-jni 0.52.1, libsignal-node 0.52.1, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-bridge-types 0.1.0, libsignal-core 0.1.0, signal-crypto 0.1.0, device-transfer 0.1.0, signal-media 0.1.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, signal-pin 0.1.0, poksho 0.7.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0</string>
<string>attest 0.1.0, libsignal-ffi 0.52.1, libsignal-jni 0.52.1, libsignal-jni-testing 0.52.1, libsignal-node 0.52.1, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-bridge-testing 0.1.0, libsignal-bridge-types 0.1.0, libsignal-core 0.1.0, signal-crypto 0.1.0, device-transfer 0.1.0, signal-media 0.1.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, signal-pin 0.1.0, poksho 0.7.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
Expand Down
5 changes: 3 additions & 2 deletions bin/update_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ def update_version(file, pattern, new_version):
RUST_PATTERN = re.compile(r'^(pub const VERSION: &str = ")(.*)(")')


def bridge_path(bridge):
return os.path.join('rust', 'bridge', bridge, 'Cargo.toml')
def bridge_path(*bridge):
return os.path.join('rust', 'bridge', *bridge, 'Cargo.toml')


VERSION_FILES = [
Expand All @@ -47,6 +47,7 @@ def bridge_path(bridge):
(os.path.join('rust', 'core', 'src', 'version.rs'), RUST_PATTERN),
(bridge_path('ffi'), CARGO_PATTERN),
(bridge_path('jni'), CARGO_PATTERN),
(bridge_path('jni', 'testing'), CARGO_PATTERN),
(bridge_path('node'), CARGO_PATTERN),
]

Expand Down
1 change: 1 addition & 0 deletions java/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ android {
packagingOptions {
jniLibs {
pickFirst 'lib/*/libsignal_jni.so'
pickFirst 'lib/*/libsignal_jni_testing.so'
}
}

Expand Down
4 changes: 2 additions & 2 deletions java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ subprojects {
spotless {
java {
target('**/*.java')
targetExclude('**/Native.java')
targetExclude('**/Native.java', '**/NativeTesting.java')
importOrder()
removeUnusedImports()

Expand Down Expand Up @@ -76,7 +76,7 @@ clean.dependsOn([cargoClean, cleanJni])

// PUBLISHING

ext.setUpSigningKey = { signingExt ->
ext.setUpSigningKey = { signingExt ->
def signingKeyId = findProperty("signingKeyId")
def signingKey = findProperty("signingKey")
def signingPassword = findProperty("signingPassword")
Expand Down
5 changes: 3 additions & 2 deletions java/build_jni.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ while [ "${1:-}" != "" ]; do
# https://github.com/rust-lang/rfcs/issues/2771
export CARGO_PROFILE_RELEASE_LTO=thin
FEATURES+=("testing-fns")
echo_then_run cargo build -p libsignal-jni --release --features "${FEATURES[*]}"
echo_then_run cargo build -p libsignal-jni -p libsignal-jni-testing --release ${FEATURES:+--features "${FEATURES[*]}"}
if [[ -z "${CARGO_BUILD_TARGET:-}" ]]; then
copy_built_library target/release signal_jni "${DESKTOP_LIB_DIR}/"
copy_built_library target/release signal_jni_testing "${DESKTOP_LIB_DIR}/"
fi
exit
;;
Expand Down Expand Up @@ -136,5 +137,5 @@ target_for_abi() {

for abi in "${android_abis[@]}"; do
rust_target=$(target_for_abi "$abi")
echo_then_run cargo build -p libsignal-jni --release ${FEATURES:+--features "${FEATURES[*]}"} -Z unstable-options --target "$rust_target" --out-dir "${ANDROID_LIB_DIR}/$abi"
echo_then_run cargo build -p libsignal-jni -p libsignal-jni-testing --release ${FEATURES:+--features "${FEATURES[*]}"} -Z unstable-options --target "$rust_target" --out-dir "${ANDROID_LIB_DIR}/$abi"
done
51 changes: 34 additions & 17 deletions java/shared/java/org/signal/libsignal/internal/Native.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Future;
import java.util.UUID;
import java.util.Map;

public final class Native {
private static void copyToTempFileAndLoad(InputStream in, String name) throws IOException {
File tempFile = Files.createTempFile(null, name).toFile();
private static Path tempDir;

private static void copyToTempDirAndLoad(InputStream in, String name) throws IOException {
// This isn't thread-safe but that's okay because it's only ever called from
// static initializers, which are themselves thread-safe.
if (tempDir == null) {
tempDir = Files.createTempDirectory("libsignal");
tempDir.toFile().deleteOnExit();
}

File tempFile = Files.createFile(tempDir.resolve(name)).toFile();
tempFile.deleteOnExit();

try (OutputStream out = new FileOutputStream(tempFile)) {
Expand All @@ -44,28 +54,35 @@ private static void copyToTempFileAndLoad(InputStream in, String name) throws IO
System.load(tempFile.getAbsolutePath());
}

/*
If libsignal_jni is embedded within this jar as a resource file, attempt
to copy it to a temporary file and then load it. This allows the jar to be
used even without a shared library existing on the filesystem.
*/
private static void loadLibrary() {
try {
String libraryName = System.mapLibraryName("signal_jni");
try (InputStream in = Native.class.getResourceAsStream("/" + libraryName)) {
if (in != null) {
copyToTempFileAndLoad(in, libraryName);
} else {
System.loadLibrary("signal_jni");
}
/**
* If the library is embedded within this jar as a resource file, attempt to
* copy it to a temporary file and then load it. This allows the jar to be
* used even without a shared library existing on the filesystem.
*
* Package-private to allow the NativeTest class to load its shared library.
* This method should only be called from a static initializer.
*/
static void loadLibrary(String name) throws IOException {
String libraryName = System.mapLibraryName(name);
try (InputStream in = Native.class.getResourceAsStream("/" + libraryName)) {
if (in != null) {
copyToTempDirAndLoad(in, libraryName);
} else {
System.loadLibrary(name);
}
}
}

private static void loadNativeCode() {
try {
loadLibrary("signal_jni");
} catch (Exception e) {
throw new RuntimeException(e);
}
}

static {
loadLibrary();
loadNativeCode();
initializeLibrary();
}

Expand Down
48 changes: 48 additions & 0 deletions java/shared/java/org/signal/libsignal/internal/NativeTesting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Copyright (C) 2024 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

// WARNING: this file was automatically generated

package org.signal.libsignal.internal;

import org.signal.libsignal.protocol.message.CiphertextMessage;
import org.signal.libsignal.protocol.state.IdentityKeyStore;
import org.signal.libsignal.protocol.state.SessionStore;
import org.signal.libsignal.protocol.state.PreKeyStore;
import org.signal.libsignal.protocol.state.SignedPreKeyStore;
import org.signal.libsignal.protocol.state.KyberPreKeyStore;
import org.signal.libsignal.protocol.groups.state.SenderKeyStore;
import org.signal.libsignal.protocol.logging.Log;
import org.signal.libsignal.protocol.logging.SignalProtocolLogger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Future;
import java.util.UUID;
import java.util.Map;

public final class NativeTesting {
private static void loadNativeCode() {
try {
Native.loadLibrary("signal_jni_testing");
} catch (Exception e) {
throw new RuntimeException(e);
}
}

static {
loadNativeCode();
}

private NativeTesting() {}

public static native int test_only_fn_returns_123();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// Copyright 2024 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

package org.signal.libsignal.internal;

import static org.junit.Assert.*;

import org.junit.Test;

public class NativeTestingTest {
@Test
public void canCallNativeTestingFns() throws Exception {
int result = NativeTesting.test_only_fn_returns_123();
assertEquals(123, result);
}
}
51 changes: 34 additions & 17 deletions rust/bridge/jni/bin/Native.java.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Future;
import java.util.UUID;
import java.util.Map;

public final class Native {
private static void copyToTempFileAndLoad(InputStream in, String name) throws IOException {
File tempFile = Files.createTempFile(null, name).toFile();
private static Path tempDir;

private static void copyToTempDirAndLoad(InputStream in, String name) throws IOException {
// This isn't thread-safe but that's okay because it's only ever called from
// static initializers, which are themselves thread-safe.
if (tempDir == null) {
tempDir = Files.createTempDirectory("libsignal");
tempDir.toFile().deleteOnExit();
}

File tempFile = Files.createFile(tempDir.resolve(name)).toFile();
tempFile.deleteOnExit();

try (OutputStream out = new FileOutputStream(tempFile)) {
Expand All @@ -44,28 +54,35 @@ public final class Native {
System.load(tempFile.getAbsolutePath());
}

/*
If libsignal_jni is embedded within this jar as a resource file, attempt
to copy it to a temporary file and then load it. This allows the jar to be
used even without a shared library existing on the filesystem.
*/
private static void loadLibrary() {
try {
String libraryName = System.mapLibraryName("signal_jni");
try (InputStream in = Native.class.getResourceAsStream("/" + libraryName)) {
if (in != null) {
copyToTempFileAndLoad(in, libraryName);
} else {
System.loadLibrary("signal_jni");
}
/**
* If the library is embedded within this jar as a resource file, attempt to
* copy it to a temporary file and then load it. This allows the jar to be
* used even without a shared library existing on the filesystem.
*
* Package-private to allow the NativeTest class to load its shared library.
* This method should only be called from a static initializer.
*/
static void loadLibrary(String name) throws IOException {
String libraryName = System.mapLibraryName(name);
try (InputStream in = Native.class.getResourceAsStream("/" + libraryName)) {
if (in != null) {
copyToTempDirAndLoad(in, libraryName);
} else {
System.loadLibrary(name);
}
}
}

private static void loadNativeCode() {
try {
loadLibrary("signal_jni");
} catch (Exception e) {
throw new RuntimeException(e);
}
}

static {
loadLibrary();
loadNativeCode();
initializeLibrary();
}

Expand Down
Loading

0 comments on commit 0e4d420

Please sign in to comment.