summaryrefslogtreecommitdiff
path: root/wrappers/java
diff options
context:
space:
mode:
authorStevenWdV <stevenwdv@gmail.com>2021-11-29 00:12:42 +0100
committerStevenWdV <stevenwdv@gmail.com>2021-11-29 00:12:42 +0100
commitf463d4c1a550c4b3dfc0be362f0b0a723a88122d (patch)
tree9b85337f1d9eda3d7070cd57d952712cfe2ae6cc /wrappers/java
parent60658378f68cc7c67cbea2758c6aef455c115c05 (diff)
Improve Java wrapper: add all tests, link correct libraries, use Maven, add Makefile & GitHub workflow. Fix make clean targets.
Diffstat (limited to 'wrappers/java')
-rw-r--r--wrappers/java/.gitignore1
-rw-r--r--wrappers/java/Makefile14
-rw-r--r--wrappers/java/README.md31
-rw-r--r--wrappers/java/pom.xml102
-rw-r--r--wrappers/java/src/main/java/nl/eduvpn/common/Discovery.java66
-rw-r--r--wrappers/java/src/main/java/nl/eduvpn/common/VerifyException.java9
-rw-r--r--wrappers/java/src/test/java/nl/eduvpn/common/VerifyTests.java78
7 files changed, 301 insertions, 0 deletions
diff --git a/wrappers/java/.gitignore b/wrappers/java/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/wrappers/java/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/wrappers/java/Makefile b/wrappers/java/Makefile
new file mode 100644
index 0000000..03e7d88
--- /dev/null
+++ b/wrappers/java/Makefile
@@ -0,0 +1,14 @@
+.PHONY: build pack test clean
+
+build:
+ mvn compile
+
+pack:
+ mvn package
+
+test:
+ $(MAKE) -C ../../exports
+ mvn test
+
+clean:
+ mvn clean
diff --git a/wrappers/java/README.md b/wrappers/java/README.md
new file mode 100644
index 0000000..87cdd49
--- /dev/null
+++ b/wrappers/java/README.md
@@ -0,0 +1,31 @@
+# Java wrapper
+
+## Requirements
+
+You will need to install JDK 8 or later ([Adoptium](https://adoptium.net/)
+or [Oracle](https://www.oracle.com/java/technologies/downloads/)). To easily compile the project, you should
+download [Maven](https://maven.apache.org/).
+
+## Build & test
+
+First build the shared Go library. Next:
+
+Build `EduVpnCommon`:
+
+```shell
+make
+```
+
+Build as JAR, including eduvpn_verify library:
+
+```shell
+make pack
+```
+
+The JAR will include all versions of the library that are built in the `exports` folder.
+
+Test:
+
+```shell
+make test
+```
diff --git a/wrappers/java/pom.xml b/wrappers/java/pom.xml
new file mode 100644
index 0000000..bd9f721
--- /dev/null
+++ b/wrappers/java/pom.xml
@@ -0,0 +1,102 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>nl.eduvpn.common</groupId>
+ <version>0.1.0</version>
+ <packaging>jar</packaging>
+
+ <artifactId>eduvpncommon</artifactId>
+ <name>eduVPN common library</name>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.10.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>5.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>5.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <!-- See com.sun.jna.Platform#getNativeLibraryResourcePrefix -->
+
+ <resource>
+ <directory>../../exports/linux/amd64</directory>
+ <includes>
+ <include>*.so</include>
+ </includes>
+ <targetPath>linux-x86-64</targetPath>
+ </resource>
+ <resource>
+ <directory>../../exports/linux/arm</directory>
+ <includes>
+ <include>*.so</include>
+ </includes>
+ <targetPath>linux-arm</targetPath>
+ </resource>
+ <resource>
+ <directory>../../exports/linux/arm64</directory>
+ <includes>
+ <include>*.so</include>
+ </includes>
+ <targetPath>linux-arm64</targetPath>
+ </resource>
+
+ <resource>
+ <directory>../../exports/windows/amd64</directory>
+ <includes>
+ <include>*.dll</include>
+ </includes>
+ <targetPath>win32-x86-64</targetPath>
+ </resource>
+ <resource>
+ <directory>../../exports/windows/386</directory>
+ <includes>
+ <include>*.dll</include>
+ </includes>
+ <targetPath>win32-x86</targetPath>
+ </resource>
+ <resource>
+ <directory>../../exports/windows/arm</directory>
+ <includes>
+ <include>*.dll</include>
+ </includes>
+ <targetPath>win32-arm</targetPath>
+ </resource>
+ <resource>
+ <directory>../../exports/windows/arm64</directory>
+ <includes>
+ <include>*.dll</include>
+ </includes>
+ <targetPath>win32-arm64</targetPath>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.22.1</version>
+ </plugin>
+ </plugins>
+ </build>
+</project>
+
diff --git a/wrappers/java/src/main/java/nl/eduvpn/common/Discovery.java b/wrappers/java/src/main/java/nl/eduvpn/common/Discovery.java
new file mode 100644
index 0000000..d73a3d2
--- /dev/null
+++ b/wrappers/java/src/main/java/nl/eduvpn/common/Discovery.java
@@ -0,0 +1,66 @@
+package nl.eduvpn.common;
+
+import com.sun.jna.*;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+
+public class Discovery {
+ private static final NativeApi discovery = Native.load("eduvpn_verify", NativeApi.class);
+
+ /**
+ * Verifies the signature on the JSON server_list.json/organization_list.json file.
+ * If the function returns the signature is valid for the given file type.
+ *
+ * @param signature .minisig signature file contents.
+ * @param signedJson Signed .json file contents.
+ * @param expectedFileName The file type to be verified, one of {@code "server_list.json"} or {@code "organization_list.json"}.
+ * @param minSignTime Minimum time for signature. Should be set to at least the time in a previously retrieved file.
+ * @throws IllegalArgumentException If {@code expectedFileName} is not one of the allowed values or one of the parameters is empty.
+ * @throws VerifyException If signature verification fails.
+ */
+ public static void verify(byte[] signature, byte[] signedJson, String expectedFileName, Instant minSignTime) throws VerifyException {
+ long err = discovery.Verify(NativeApi.GoSlice.make(signature), NativeApi.GoSlice.make(signedJson),
+ NativeApi.GoSlice.make(expectedFileName.getBytes(StandardCharsets.UTF_8)),
+ minSignTime.getEpochSecond());
+ if (err != 0) {
+ if (err == 1) throw new IllegalArgumentException("Unknown excpectedFileName");
+ throw new VerifyException(err);
+ }
+ }
+
+ /**
+ * Use for testing only, see Go documentation.
+ */
+ // package-private
+ static void insecureTestingSetExtraKey(String keyString) {
+ discovery.InsecureTestingSetExtraKey(NativeApi.GoSlice.make(keyString.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ private interface NativeApi extends Library {
+ @Structure.FieldOrder({"data", "len", "cap"})
+ class GoSlice extends Structure implements Structure.ByValue {
+ public Pointer data;
+ public long len, cap;
+
+ public GoSlice(Pointer data, long len, long cap) {
+ this.data = data;
+ this.len = len;
+ this.cap = cap;
+ }
+
+ public static GoSlice make(byte[] bytes) {
+ Memory memory = new Memory(bytes.length);
+ memory.write(0, bytes, 0, bytes.length);
+ return new GoSlice(memory, bytes.length, bytes.length);
+ }
+ }
+
+ long Verify(GoSlice signatureFileContent, GoSlice signedJson, GoSlice expectedFileName, long minSignTime);
+
+ void InsecureTestingSetExtraKey(GoSlice keyString);
+ }
+
+ private Discovery() {
+ }
+}
diff --git a/wrappers/java/src/main/java/nl/eduvpn/common/VerifyException.java b/wrappers/java/src/main/java/nl/eduvpn/common/VerifyException.java
new file mode 100644
index 0000000..83dffb1
--- /dev/null
+++ b/wrappers/java/src/main/java/nl/eduvpn/common/VerifyException.java
@@ -0,0 +1,9 @@
+package nl.eduvpn.common;
+
+public class VerifyException extends Exception {
+ public final long code; //TODO not use plain long
+
+ public VerifyException(long code) {
+ this.code = code;
+ }
+} \ No newline at end of file
diff --git a/wrappers/java/src/test/java/nl/eduvpn/common/VerifyTests.java b/wrappers/java/src/test/java/nl/eduvpn/common/VerifyTests.java
new file mode 100644
index 0000000..b4767a5
--- /dev/null
+++ b/wrappers/java/src/test/java/nl/eduvpn/common/VerifyTests.java
@@ -0,0 +1,78 @@
+package nl.eduvpn.common;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class VerifyTests {
+ private static final Path testDataDir = Paths.get("../../test_data");
+
+ @SuppressWarnings("OptionalGetWithoutIsPresent")
+ @BeforeAll
+ static void oneTimeSetup() throws IOException {
+ Discovery.insecureTestingSetExtraKey(Files.lines(testDataDir.resolve("dummy/public.key")).reduce((a, b) -> b).get());
+ }
+
+ @Test
+ void testValid() {
+ assertDoesNotThrow(() ->
+ Discovery.verify(
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json.minisig")),
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json")),
+ "server_list.json",
+ Instant.EPOCH
+ ));
+ }
+
+ @Test
+ void testInvalidSignature() {
+ Assertions.assertEquals(2, assertThrows(VerifyException.class, () ->
+ Discovery.verify(
+ Files.readAllBytes(testDataDir.resolve("dummy/random.txt")),
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json")),
+ "server_list.json",
+ Instant.EPOCH
+ )).code);
+ }
+
+ @Test
+ void testWrongKey() {
+ assertEquals(3, assertThrows(VerifyException.class, () ->
+ Discovery.verify(
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json.wrong_key.minisig")),
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json")),
+ "server_list.json",
+ Instant.EPOCH
+ )).code);
+ }
+
+ @Test
+ void testOldSignature() {
+ assertEquals(4, assertThrows(VerifyException.class, () ->
+ Discovery.verify(
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json.minisig")),
+ Files.readAllBytes(testDataDir.resolve("dummy/server_list.json")),
+ "server_list.json",
+ Instant.MAX
+ )).code);
+ }
+
+ @Test
+ void testUnknownExpectedFile() {
+ assertThrows(IllegalArgumentException.class, () ->
+ Discovery.verify(
+ Files.readAllBytes(testDataDir.resolve("dummy/other_list.json.minisig")),
+ Files.readAllBytes(testDataDir.resolve("dummy/other_list.json")),
+ "other_list.json",
+ Instant.EPOCH
+ ));
+ }
+} \ No newline at end of file