OSDN Git Service

Added language bindings for Java. MT
authorLoRd_MuldeR <mulder2@gmx.de>
Mon, 24 Jun 2024 20:12:35 +0000 (22:12 +0200)
committerLoRd_MuldeR <mulder2@gmx.de>
Thu, 4 Jul 2024 21:02:49 +0000 (23:02 +0200)
22 files changed:
.gitignore
binding/java/.classpath [new file with mode: 0644]
binding/java/.project [new file with mode: 0644]
binding/java/build.xml [new file with mode: 0644]
binding/java/ivy.xml [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/AbstractSlunkCrypt.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/SlunkCrypt.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptDecryptor.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptEncryptor.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptException.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptLibrary.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptParam.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/types/Context.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/types/SizeT.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt16.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64ByReference.java [new file with mode: 0644]
binding/java/src/com/muldersoft/slunkcrypt/internal/utilities/Constant.java [new file with mode: 0644]
binding/java/src/overview.html [new file with mode: 0644]
binding/java/tests/src/com/muldersoft/slunkcrypt/tests/ContextTest.java [new file with mode: 0644]
binding/java/tests/src/com/muldersoft/slunkcrypt/tests/FunctionTest.java [new file with mode: 0644]
binding/java/tests/src/com/muldersoft/slunkcrypt/tests/Utilities.java [new file with mode: 0644]

index 4b6a6da..8e70ebf 100644 (file)
@@ -1,8 +1,11 @@
 *.html
 *.user
+/**/.settings
+/**/.vs
 /**/bin
+/**/dist
+/**/doc
 /**/lib
 /**/obj
+/**/out
 /**/target
-/.vs
-/out
diff --git a/binding/java/.classpath b/binding/java/.classpath
new file mode 100644 (file)
index 0000000..fbeed7e
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" output="tests/bin" path="tests/src">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+       <classpathentry kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=SlunkCrypt&amp;ivyXmlPath=ivy.xml&amp;confs=*"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/binding/java/.project b/binding/java/.project
new file mode 100644 (file)
index 0000000..ee256f6
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>SlunkCrypt</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.apache.ivyde.eclipse.ivynature</nature>
+       </natures>
+</projectDescription>
diff --git a/binding/java/build.xml b/binding/java/build.xml
new file mode 100644 (file)
index 0000000..3864f1e
--- /dev/null
@@ -0,0 +1,73 @@
+<project xmlns:ivy="antlib:org.apache.ivy.ant" name="SlunkCrypt" default="default" basedir=".">
+       <property name="bin.dir" location="bin"/>
+       <property name="doc.dir" location="doc"/>
+       <property name="lib.dir" location="lib"/>
+       <property name="out.dir" location="out"/>
+       <property name="src.dir" location="src"/>
+       <property name="test.src.dir" location="tests/src"/>
+       <property name="test.bin.dir" location="tests/bin"/>
+       <property name="test.out.dir" location="tests/out"/>
+
+       <target name="default" depends="clean,dist"/>
+
+       <target name="retrieve">
+               <ivy:retrieve pattern="${lib.dir}/[conf]/[artifact]-[revision].[ext]" conf="compile" pathid="compile.classpath"/>
+       </target>
+
+       <target name="retrieve.test">
+               <ivy:retrieve pattern="${lib.dir}/[conf]/[artifact]-[revision].[ext]" conf="test" pathid="test.classpath"/>
+       </target>
+
+       <target name="compile" depends="retrieve">
+               <mkdir dir="${bin.dir}"/>
+               <javac srcdir="${src.dir}" destdir="${bin.dir}" includeantruntime="false">
+                       <classpath refid="compile.classpath"/>
+               </javac>
+       </target>
+
+       <target name="compile.test" depends="compile,retrieve.test">
+               <mkdir dir="${test.bin.dir}"/>
+               <javac srcdir="${test.src.dir}" destdir="${test.bin.dir}" includeantruntime="false">
+                       <classpath>
+                               <path refid="test.classpath"/>
+                               <pathelement location="${bin.dir}"/>
+                       </classpath>
+               </javac>
+       </target>
+
+       <target name="doc" depends="retrieve">
+               <javadoc sourcepath="${src.dir}" destdir="${doc.dir}" packagenames="com.muldersoft.slunkcrypt" public="true" overview="${src.dir}/overview.html" doctitle="SlunkCrypt" windowtitle="SlunkCrypt">
+                       <classpath refid="compile.classpath"/>
+               </javadoc>
+       </target>
+
+       <target name="dist" depends="compile,doc">
+               <mkdir dir="${out.dir}"/>
+               <jar jarfile="${out.dir}/slunkcrypt-1.jar" basedir="${bin.dir}"/>
+               <jar jarfile="${out.dir}/slunkcrypt-1-javadoc.jar" basedir="${doc.dir}"/>
+       </target>
+
+       <target name="test" depends="compile.test">
+               <mkdir dir="${test.out.dir}"/>
+               <junitlauncher haltOnFailure="true" printSummary="true">
+                       <classpath>
+                               <path refid="test.classpath"/>
+                               <pathelement location="${bin.dir}"/>
+                               <pathelement location="${test.bin.dir}"/>
+                       </classpath>
+                       <testclasses outputdir="${test.out.dir}">
+                               <fileset dir="${test.bin.dir}" includes="**/*Test.class"/>
+                       </testclasses>
+                       <listener type="legacy-xml"/>
+               </junitlauncher>
+       </target>
+
+       <target name="clean">
+               <delete dir="${bin.dir}"/>
+               <delete dir="${doc.dir}"/>
+               <delete dir="${lib.dir}"/>
+               <delete dir="${out.dir}"/>
+               <delete dir="${test.bin.dir}"/>
+               <delete dir="${test.out.dir}"/>
+       </target>
+</project>
diff --git a/binding/java/ivy.xml b/binding/java/ivy.xml
new file mode 100644 (file)
index 0000000..4b51095
--- /dev/null
@@ -0,0 +1,14 @@
+<ivy-module version="2.0">
+       <info organisation="com.muldersoft" module="slunkcrypt-jna"/>
+       <configurations>
+               <conf name="compile" visibility="public"/>
+               <conf name="test" visibility="private" extends="compile"/>
+       </configurations>
+       <dependencies>
+               <dependency org="net.java.dev.jna" name="jna" rev="5.14.0" conf="compile->default"/>
+               <dependency org="org.junit.jupiter" name="junit-jupiter-api" rev="5.10.3" conf="test->default"/>
+               <dependency org="org.junit.platform" name="junit-platform-engine" rev="1.10.3" conf="test->default"/>
+               <dependency org="org.junit.platform" name="junit-platform-launcher" rev="1.10.3" conf="test->default"/>
+               <dependency org="org.junit.jupiter" name="junit-jupiter-engine" rev="5.10.3" conf="test->default"/>
+       </dependencies>
+</ivy-module>
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/AbstractSlunkCrypt.java b/binding/java/src/com/muldersoft/slunkcrypt/AbstractSlunkCrypt.java
new file mode 100644 (file)
index 0000000..3b8cae1
--- /dev/null
@@ -0,0 +1,158 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.muldersoft.slunkcrypt.SlunkCryptException.ErrorKind;
+import com.muldersoft.slunkcrypt.internal.SlunkCryptLibrary;
+import com.muldersoft.slunkcrypt.internal.SlunkCryptParam;
+import com.muldersoft.slunkcrypt.internal.types.Context;
+import com.muldersoft.slunkcrypt.internal.types.SizeT;
+import com.muldersoft.slunkcrypt.internal.types.UInt64;
+
+/**
+ * The abstract base class for all {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} implementations.
+ */
+abstract class AbstractSlunkCrypt implements SlunkCrypt {
+
+       private static final int PASSWD_LENGTH_MIN =   8;
+       private static final int PASSWD_LENGTH_MAX = 256;
+
+       private static final String PROPERTY_THREAD_COUNT = "slunkcrypt.thread_count";
+
+       private static final int SLUNKCRYPT_ENCRYPT = 0;
+       private static final int SLUNKCRYPT_DECRYPT = 1;
+       
+       protected enum Mode {
+               Encrypt(SLUNKCRYPT_ENCRYPT),
+               Decrypt(SLUNKCRYPT_DECRYPT);
+
+               private final int value;
+
+               private Mode(final int value) {
+                       this.value = value;
+               }
+
+               public int intValue() {
+                       return value;
+               }
+       }
+
+       private final Context context;
+       private final AtomicBoolean closed = new AtomicBoolean(false);
+
+       protected AbstractSlunkCrypt(final String passwd, final long nonce, final Mode mode) throws SlunkCryptException {
+               this(Objects.requireNonNull(passwd, "passwd").getBytes(StandardCharsets.UTF_8), nonce, mode);
+       }
+
+       protected AbstractSlunkCrypt(final byte[] passwd, final long nonce, final Mode mode) throws SlunkCryptException {
+               if ((mode != Mode.Encrypt) && (mode != Mode.Decrypt)) {
+                       throw new IllegalArgumentException("mode");
+               }
+               if ((passwd == null) || (passwd.length < PASSWD_LENGTH_MIN) || (passwd.length > PASSWD_LENGTH_MAX)) {
+                       throw new IllegalArgumentException("passwd");
+               }
+
+               final SlunkCryptParam parameters = new SlunkCryptParam();
+               try {
+                       final String threadCount = System.getProperty(PROPERTY_THREAD_COUNT);
+                       if ((threadCount != null) && (!threadCount.isEmpty())) {
+                               parameters.thread_count = SizeT.of(Long.parseUnsignedLong(threadCount));
+                       }
+               } catch(final Exception e) {
+                       throw new IllegalArgumentException(PROPERTY_THREAD_COUNT, e);
+               }
+
+               context = SlunkCryptLibrary.INSTANCE.slunkcrypt_alloc_ext(UInt64.of(nonce), passwd, SizeT.of(passwd.length), mode.intValue(), parameters);
+               if (context.equals(Context.NULL)) {
+                       throw new SlunkCryptException(ErrorKind.Failure, "Failed to create SlunkCrypt context!");
+               }
+       }
+
+       @Override
+       public final byte[] process(final byte[] input) throws SlunkCryptException {
+               if (input == null) {
+                       throw new NullPointerException("input");
+               }
+
+               final byte[] output = new byte[input.length];
+               process(input, output);
+
+               return output;
+       }
+
+       @Override
+       public final void process(final byte[] input, final byte[] output) throws SlunkCryptException {
+               if (input == null) {
+                       throw new NullPointerException("input");
+               }
+               if ((output == null) || (output.length < input.length)) {
+                       throw new IllegalArgumentException("output");
+               }
+
+               checkState();
+
+               final int errorCode = SlunkCryptLibrary.INSTANCE.slunkcrypt_process(context, input, output, SizeT.of(input.length));
+               if (!SlunkCryptException.isSuccess(errorCode)) {
+                       throw SlunkCryptException.mapErrorCode(errorCode, "Failed to process data!");
+               }
+       }
+
+       @Override
+       public final void inplace(final byte[] buffer) throws SlunkCryptException {
+               if (buffer == null) {
+                       throw new NullPointerException("input");
+               }
+
+               checkState();
+
+               final int errorCode = SlunkCryptLibrary.INSTANCE.slunkcrypt_inplace(context, buffer, SizeT.of(buffer.length));
+               if (!SlunkCryptException.isSuccess(errorCode)) {
+                       throw SlunkCryptException.mapErrorCode(errorCode, "Failed to process data!");
+               }
+       }
+
+       protected void reset(final String passwd, final long nonce, final Mode mode) throws SlunkCryptException {
+               reset(Objects.requireNonNull(passwd, "passwd").getBytes(StandardCharsets.UTF_8), nonce, mode);
+       }
+
+       protected void reset(final byte[] passwd, final long nonce, final Mode mode) throws SlunkCryptException {
+               if ((mode != Mode.Encrypt) && (mode != Mode.Decrypt)) {
+                       throw new IllegalArgumentException("mode");
+               }
+               if ((passwd == null) || (passwd.length < PASSWD_LENGTH_MIN) || (passwd.length > PASSWD_LENGTH_MAX)) {
+                       throw new IllegalArgumentException("passwd");
+               }
+
+               checkState();
+
+               final int errorCode = SlunkCryptLibrary.INSTANCE.slunkcrypt_reset(context, UInt64.of(nonce), passwd, SizeT.of(passwd.length), mode.ordinal());
+               if (!SlunkCryptException.isSuccess(errorCode)) {
+                       throw SlunkCryptException.mapErrorCode(errorCode, "Failed to process data!");
+               }
+       }
+
+       @Override
+       public void close() {
+               if (closed.compareAndSet(false, true)) {
+                       SlunkCryptLibrary.INSTANCE.slunkcrypt_free(context);
+               }
+       }
+
+       @Override
+       public String toString() {
+               return (!closed.get()) ? String.format("SlunkCrypt(0x%016X)", context.longValue()) : "SlunkCrypt(NULL)";
+       }
+
+       protected void checkState() {
+               if (closed.get()) {
+                       throw new IllegalStateException("Instance is already closed!");
+               }
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/SlunkCrypt.java b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCrypt.java
new file mode 100644 (file)
index 0000000..cf6e40c
--- /dev/null
@@ -0,0 +1,138 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import com.muldersoft.slunkcrypt.internal.SlunkCryptLibrary;
+import com.muldersoft.slunkcrypt.internal.types.UInt64ByReference;
+
+/**
+ * SlunkCrypt context for encryption or decryption.
+ * <p>Implementations of this interface wrap the &ldquo;native&rdquo; SlunkCrypt context and they provide the instance methods for {@link SlunkCrypt#process(byte[]) processing}, i.e. encrypting or decrypting, chunks of data.</p>
+ * <p>Separate implementations for {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor <i>encryption</i>} mode and {@link com.muldersoft.slunkcrypt.SlunkCryptDecryptor <i>decryption</i>} mode are available.</p>
+ * <p><b><i>Warning:</i></b> The wrapped &ldquo;native&rdquo; SlunkCrypt context must be released by calling the {@link SlunkCrypt#close close()} method, or otherwise a resource leak is imminent! Using <a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html" target="_blank">try-with-resources</a> is recommended.</p>
+ * <h3>System properties:</h3>
+ * <table summary="System properties" border>
+ *     <tr>
+ *         <th><b>Property</b></th>
+ *         <th><b>Meaning</b></th>
+ *     </tr>
+ *     <tr>
+ *         <td><tt>slunkcrypt.thread_count</tt></td>
+ *         <td>The number of threads to be used internally by SlunkCrypt; a value of <b><tt>0</tt></b> means auto-detection (the default)</td>
+ *     </tr>
+ * </table>
+ * <h2>Thread Safety</h2>
+ * <p>Separate <tt>SlunkCrypt</tt> instances can safely be accessed from concurrent threads <i>without</i> the need for any kind if synchronization, provided that each instance is only accessed by the single thread that has created the respective instance. In order to <i>share</i> the same <tt>SlunkCrypt</tt> instance between concurrent threads, every access to the &ldquo;shared&rdquo; instance must be synchronized explicitly by the callers!</p>
+ * <h2>Example</h2>
+ * <pre>{@code
+ * public class Main {
+ * 
+ *     private static String PASSPHRASE = "OrpheanBeholderScryDoubt";
+ * 
+ *     public static void main(String[] args) throws Exception {
+ *         // Fill buffer with random data (plaintext)
+ *         final byte[] buffer = new byte[64];
+ *         ThreadLocalRandom.current().nextBytes(buffer);
+ *             
+ *         // Encrypt the data in-place
+ *         final long nonce;
+ *         try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+ *             nonce = instance.getNonce();
+ *             instance.inplace(buffer);
+ *         }
+ * 
+ *         // Decrypt the data in-place
+ *         try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE, nonce)) {
+ *             instance.inplace(buffer);
+ *         }
+ *     }
+ * }}</pre>
+ */
+public interface SlunkCrypt extends AutoCloseable {
+
+       /**
+        * Process, i&#46;e&#46; encrypt or decrypt, the next chunk of data.
+        * <p>This method is designed for stream processing. It is supposed to be called repeatedly, until all input data has been processed.</p>
+        * @param input the input buffer to be read; will <i>not</i> be modified
+        * @return the new buffer with the result
+        * @throws IllegalStateException the instance was already closed
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       byte[] process(byte[] input) throws SlunkCryptException;
+
+       /**
+        * Process, i&#46;e&#46; encrypt or decrypt, the next chunk of data.
+        * <p>This method is designed for stream processing. It is supposed to be called repeatedly, until all input data has been processed.</p>
+        * <p><i>Note:</i> The size of the output buffer must be at least the size of the input buffer!</p>
+        * @param input the input buffer to be read; will <i>not</i> be modified
+        * @param output the output buffer to write the result to
+        * @throws IllegalStateException the instance was already closed
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       void process(byte[] input, byte[] output) throws SlunkCryptException;
+
+       /**
+        * Process, i&#46;e&#46; encrypt or decrypt, the next chunk of data <i>in-place</i>.
+        * <p>This method is designed for stream processing. It is supposed to be called repeatedly, until all input data has been processed.</p>
+        * @param buffer the input/output buffer to be updated
+        * @throws IllegalStateException the instance was already closed
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       void inplace(byte[] buffer) throws SlunkCryptException;
+
+       /**
+        * Generate a new random nonce.
+        * <p><i>Note:</i> The nonce is generating using a &ldquo;strong&rdquo; cryptographically secure pseudorandom number generator.</p>
+        * @return The new nonce value
+        * @throws SlunkCryptException the operation failed or it was aborted
+        */
+       public static long generateNonce() throws SlunkCryptException {
+               final UInt64ByReference nonce = new UInt64ByReference();
+               final int errorCode = SlunkCryptLibrary.INSTANCE.slunkcrypt_generate_nonce(nonce);
+               if (!SlunkCryptException.isSuccess(errorCode)) {
+                       throw SlunkCryptException.mapErrorCode(errorCode, "Failed to generate nonce!");
+               }
+               return nonce.getValue();
+       }
+
+       /**
+        * Get the version of the &ldquo;native&rdquo; SlunkCrypt library.
+        * @return The version number as <tt>Map&lt;String, Short&gt;</tt>, containing the keys: <ul><li><tt>"slunkcrypt.version.major"</tt>,<li> <tt>"slunkcrypt.version.minor"</tt>, <li><tt>"slunkcrypt.version.patch"</tt></ul>
+        */
+       public static Map<String, Short> getVersion() {
+               final Map<String, Short> version = new LinkedHashMap<String, Short>(3);
+               version.put(SlunkCryptLibrary.Version.MAJOR.getSymbolName().toLowerCase(Locale.US).replace('_', '.'), SlunkCryptLibrary.Version.MAJOR.getValue().shortValue());
+               version.put(SlunkCryptLibrary.Version.MINOR.getSymbolName().toLowerCase(Locale.US).replace('_', '.'), SlunkCryptLibrary.Version.MINOR.getValue().shortValue());
+               version.put(SlunkCryptLibrary.Version.PATCH.getSymbolName().toLowerCase(Locale.US).replace('_', '.'), SlunkCryptLibrary.Version.PATCH.getValue().shortValue());
+               return Collections.unmodifiableMap(version);
+       }
+
+       /**
+        * Get the build string of the &ldquo;native&rdquo; SlunkCrypt library.
+        * @return The build string, containing the build time and date, e.g. <tt>"Jun 24 2024, 21:55:08"</tt>
+        */
+       public static String getBuild() {
+               return SlunkCryptLibrary.Version.BUILD.getValue();
+       }
+
+       /**
+        * Close the &ldquo;native&rdquo; SlunkCrypt context wrapped by this {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance and release any system resources associated with it.
+        * <p><b><i>Warning:</i></b> Once this method has been called, this instance is left in an &ldquo;invalidated&rdquo; state and any attempt to process more data is going to throw an <tt>IllegalStateException</tt>!</p>
+        */
+       @Override
+       void close();
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptDecryptor.java b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptDecryptor.java
new file mode 100644 (file)
index 0000000..fedc19d
--- /dev/null
@@ -0,0 +1,59 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+/**
+ * SlunkCrypt <i>decryption</i> context.
+ * <p>Wraps a &ldquo;native&rdquo; SlunkCrypt context that was initialized in <b><i>decryption</i></b> mode and implements the {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} interface.</p>
+ */
+public class SlunkCryptDecryptor extends AbstractSlunkCrypt {
+
+       /**
+        * Create a new {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance in <b><i>decryption</i></b> mode.
+        * @param passwd the password to be used for decryption (UTF-8)
+        * @param nonce the nonce to be used for decryption
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        */
+       public SlunkCryptDecryptor(final String passwd, final long nonce) throws SlunkCryptException {
+               super(passwd, nonce, Mode.Decrypt);
+       }
+
+       /**
+        * Create a new {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance in <b><i>decryption</i></b> mode.
+        * @param passwd the password to be used for decryption (binary)
+        * @param nonce the nonce to be used for decryption
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        */
+       public SlunkCryptDecryptor(final byte[] passwd, final long nonce) throws SlunkCryptException {
+               super(passwd, nonce, Mode.Decrypt);
+       }
+
+       /**
+        * Re-initialize this {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor SlunkCryptDecryptor} instance.
+        * @param passwd the password to be used for decryption (UTF-8)
+        * @param nonce the nonce to be used for decryption
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       public void reset(final String passwd, final long nonce) throws SlunkCryptException {
+               reset(passwd, nonce, Mode.Decrypt);
+       }
+
+       /**
+        * Re-initialize this {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor SlunkCryptDecryptor} instance.
+        * @param passwd the password to be used for decryption (binary)
+        * @param nonce the nonce to be used for decryption
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       public void reset(final byte[] passwd, final long nonce) throws SlunkCryptException {
+               reset(passwd, nonce , Mode.Decrypt);
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptEncryptor.java b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptEncryptor.java
new file mode 100644 (file)
index 0000000..a08c3bc
--- /dev/null
@@ -0,0 +1,79 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+/**
+ * SlunkCrypt <i>encryption</i> context.
+ * <p>Wraps a &ldquo;native&rdquo; SlunkCrypt context that was initialized in <b><i>encryption</i></b> mode and implements the {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} interface.</p>
+ */
+public class SlunkCryptEncryptor extends AbstractSlunkCrypt {
+
+       private long nonce;
+
+       /**
+        * Create a new {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance in <b><i>encryption</i></b> mode.
+        * <p><i>Note:</i> This method implicitly generates a new random nonce. Use {@link SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.</p>
+        * @param passwd the password to be used for encryption (UTF-8)
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        */
+       public SlunkCryptEncryptor(final String passwd) throws SlunkCryptException {
+               this(passwd, SlunkCrypt.generateNonce());
+       }
+
+       /**
+        * Create a new {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance in <b><i>encryption</i></b> mode.
+        * <p><i>Note:</i> This method implicitly generates a new random nonce. Use {@link SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.</p>
+        * @param passwd the password to be used for encryption (binary)
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        */
+       public SlunkCryptEncryptor(final byte[] passwd) throws SlunkCryptException {
+               this(passwd, SlunkCrypt.generateNonce());
+       }
+
+       private SlunkCryptEncryptor(final String passwd, final long nonce) throws SlunkCryptException {
+               super(passwd, nonce, Mode.Encrypt);
+               this.nonce = nonce;
+       }
+
+       private SlunkCryptEncryptor(final byte[] passwd, final long nonce) throws SlunkCryptException {
+               super(passwd, nonce, Mode.Encrypt);
+               this.nonce = nonce;
+       }
+
+       /**
+        * Re-initialize this {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor SlunkCryptEncryptor} instance.
+        * <p><i>Note:</i> This method implicitly generates a new random nonce. Use {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.</p>
+        * @param passwd the password to be used for encryption (UTF-8)
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       public void reset(final String passwd) throws SlunkCryptException {
+               reset(passwd, nonce = SlunkCrypt.generateNonce(), Mode.Encrypt);
+       }
+
+       /**
+        * Re-initialize this {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor SlunkCryptEncryptor} instance.
+        * <p><i>Note:</i> This method implicitly generates a new random nonce. Use {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.</p>
+        * @param passwd the password to be used for encryption (binary)
+        * @throws SlunkCryptException the operation failed or it was aborted
+        * @throws IllegalArgumentException invalid parameters detected
+        * @throws IllegalStateException the instance has already been closed
+        */
+       public void reset(final byte[] passwd) throws SlunkCryptException {
+               reset(passwd, nonce = SlunkCrypt.generateNonce(), Mode.Encrypt);
+       }
+
+       /**
+        * Return the nonce value that was generated for this {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor SlunkCryptEncryptor} instance.
+        * @return the nonce value
+        */
+       public long getNonce() {
+               return nonce;
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptException.java b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptException.java
new file mode 100644 (file)
index 0000000..ce72471
--- /dev/null
@@ -0,0 +1,68 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Signals that a SlunkCrypt operation has failed or that it has been aborted by the user.
+ * <p>Details are provided via {@link ErrorKind}.</p>
+ */
+public final class SlunkCryptException extends IOException {
+
+       /**
+        * The kind of error that has triggered a {@link SlunkCryptException}.
+        */
+       public enum ErrorKind {
+               /** The operation completed successfully. */
+               Success,
+               /** The operation has failed. */
+               Failure,
+               /** The operation was aborted by the user. */
+               Aborted,
+               /** An unknown kind of error has occurred. */
+               Unknown
+       }
+
+       private static final long serialVersionUID = 1L;
+       
+       private final ErrorKind kind;
+
+       private static final int SLUNKCRYPT_SUCCESS =  0;
+       private static final int SLUNKCRYPT_FAILURE = -1;
+       private static final int SLUNKCRYPT_ABORTED = -2;
+
+       SlunkCryptException(final ErrorKind kind, final String message) {
+               super(message);
+               this.kind = Objects.requireNonNull(kind);
+       }
+
+       static boolean isSuccess(final int errorCode) {
+               return errorCode == SLUNKCRYPT_SUCCESS;
+       }
+
+       static SlunkCryptException mapErrorCode(final int errorCode, final String message) {
+               switch(errorCode) {
+                       case SLUNKCRYPT_SUCCESS:
+                               return new SlunkCryptException(ErrorKind.Success, message);
+                       case SLUNKCRYPT_FAILURE:
+                               return new SlunkCryptException(ErrorKind.Failure, message);
+                       case SLUNKCRYPT_ABORTED:
+                               return new SlunkCryptException(ErrorKind.Aborted, message);
+                       default:
+                               return new SlunkCryptException(ErrorKind.Unknown, message);
+               }
+       }
+
+       /**
+        * Get the kind of error that has triggered this exception.
+        * @return the {@link com.muldersoft.slunkcrypt.SlunkCryptException.ErrorKind ErrorKind}
+        */
+       public ErrorKind getErrorKind() {
+               return kind;
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptLibrary.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptLibrary.java
new file mode 100644 (file)
index 0000000..87e2ba6
--- /dev/null
@@ -0,0 +1,35 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal;
+
+import com.muldersoft.slunkcrypt.internal.types.Context;
+import com.muldersoft.slunkcrypt.internal.types.SizeT;
+import com.muldersoft.slunkcrypt.internal.types.UInt64;
+import com.muldersoft.slunkcrypt.internal.types.UInt64ByReference;
+import com.muldersoft.slunkcrypt.internal.utilities.Constant;
+import com.muldersoft.slunkcrypt.internal.utilities.Constant.StrPtrConst;
+import com.muldersoft.slunkcrypt.internal.utilities.Constant.UInt16Const;
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+public interface SlunkCryptLibrary extends Library {
+
+       static final SlunkCryptLibrary INSTANCE = Native.load("slunkcrypt-1", SlunkCryptLibrary.class);
+
+       static class Version {
+               public static final UInt16Const MAJOR = Constant.ofUInt16(INSTANCE, "SLUNKCRYPT_VERSION_MAJOR");
+               public static final UInt16Const MINOR = Constant.ofUInt16(INSTANCE, "SLUNKCRYPT_VERSION_MINOR");
+               public static final UInt16Const PATCH = Constant.ofUInt16(INSTANCE, "SLUNKCRYPT_VERSION_PATCH");
+               public static final StrPtrConst BUILD = Constant.ofStrPtr(INSTANCE, "SLUNKCRYPT_BUILD");
+       }
+
+       Context slunkcrypt_alloc_ext(UInt64 nonce, byte[] passwd, SizeT passwd_len, int mode, SlunkCryptParam param);
+       int slunkcrypt_reset(Context context, UInt64 nonce, byte[] passwd, SizeT passwd_len, int mode);
+       void slunkcrypt_free(Context context);
+       int slunkcrypt_inplace(Context context, byte[] buffer, SizeT data_len);
+       int slunkcrypt_process(Context context, byte[] input, byte[] output, SizeT data_len);
+       int slunkcrypt_generate_nonce(UInt64ByReference nonce);
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptParam.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptParam.java
new file mode 100644 (file)
index 0000000..a7c7a7d
--- /dev/null
@@ -0,0 +1,21 @@
+package com.muldersoft.slunkcrypt.internal;
+
+import com.muldersoft.slunkcrypt.internal.types.SizeT;
+import com.muldersoft.slunkcrypt.internal.types.UInt16;
+import com.sun.jna.Structure;
+import com.sun.jna.Structure.FieldOrder;
+
+@FieldOrder({ "version", "thread_count", "legacy_compat", "debug_logging" })
+public class SlunkCryptParam extends Structure {
+       
+       public static final UInt16 SLUNK_PARAM_VERSION = UInt16.of((short)2);
+
+       public SlunkCryptParam() {
+               version = SLUNK_PARAM_VERSION;
+       }
+
+       public UInt16 version;
+       public SizeT thread_count;
+       public int legacy_compat;
+       public int debug_logging;
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/types/Context.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/Context.java
new file mode 100644 (file)
index 0000000..2a81165
--- /dev/null
@@ -0,0 +1,29 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal.types;
+
+import com.sun.jna.IntegerType;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+
+public class Context extends IntegerType {
+
+       private static final long serialVersionUID = 1L;
+
+       public static final Context NULL = new Context();
+
+       public Context() {
+               super(Native.POINTER_SIZE, Pointer.nativeValue(Pointer.NULL), true);
+       }
+
+       public Context(final long value) {
+               super(Native.POINTER_SIZE, value, true);
+       }
+
+       public Pointer toPointer() {
+               return Pointer.createConstant(longValue());
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/types/SizeT.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/SizeT.java
new file mode 100644 (file)
index 0000000..dc959d7
--- /dev/null
@@ -0,0 +1,26 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal.types;
+
+import com.sun.jna.IntegerType;
+import com.sun.jna.Native;
+
+public class SizeT extends IntegerType {
+
+       private static final long serialVersionUID = 1L;
+       
+       public SizeT() {
+               super(Native.SIZE_T_SIZE);
+       }
+       
+       private SizeT(final long value) {
+               super(Native.SIZE_T_SIZE, value, true);
+       }
+       
+       public static SizeT of(final long value) {
+               return new SizeT(value);
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt16.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt16.java
new file mode 100644 (file)
index 0000000..e57316e
--- /dev/null
@@ -0,0 +1,25 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal.types;
+
+import com.sun.jna.IntegerType;
+
+public class UInt16 extends IntegerType {
+
+       private static final long serialVersionUID = 1L;
+       
+       public UInt16() {
+               super(Short.BYTES);
+       }
+       
+       private UInt16(final long value) {
+               super(Short.BYTES, value, true);
+       }
+       
+       public static UInt16 of(final long value) {
+               return new UInt16(value);
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64.java
new file mode 100644 (file)
index 0000000..6924f6d
--- /dev/null
@@ -0,0 +1,25 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal.types;
+
+import com.sun.jna.IntegerType;
+
+public class UInt64 extends IntegerType {
+
+       private static final long serialVersionUID = 1L;
+       
+       public UInt64() {
+               super(Long.BYTES);
+       }
+       
+       private UInt64(final long value) {
+               super(Long.BYTES, value, true);
+       }
+
+       public static UInt64 of(final long value) {
+               return new UInt64(value);
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64ByReference.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64ByReference.java
new file mode 100644 (file)
index 0000000..d617c68
--- /dev/null
@@ -0,0 +1,32 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal.types;
+
+import com.sun.jna.ptr.ByReference;
+
+public class UInt64ByReference extends ByReference {
+
+       public UInt64ByReference() {
+               super(Long.BYTES);
+       }
+       
+       private UInt64ByReference(final long value) {
+               super(Long.BYTES);
+               setValue(value);
+       }
+
+       public void setValue(final long value) {
+               getPointer().setLong(0, value);
+       }
+       
+       public long getValue() {
+               return getPointer().getLong(0);
+       }
+
+       public static UInt64ByReference of(final long value) {
+               return new UInt64ByReference(value);
+       }
+}
diff --git a/binding/java/src/com/muldersoft/slunkcrypt/internal/utilities/Constant.java b/binding/java/src/com/muldersoft/slunkcrypt/internal/utilities/Constant.java
new file mode 100644 (file)
index 0000000..9a6648a
--- /dev/null
@@ -0,0 +1,73 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.internal.utilities;
+
+import java.lang.reflect.Proxy;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.WeakHashMap;
+
+import com.muldersoft.slunkcrypt.internal.types.UInt16;
+import com.sun.jna.Library;
+import com.sun.jna.Library.Handler;
+import com.sun.jna.NativeLibrary;
+import com.sun.jna.Pointer;
+
+public class Constant {
+
+       private static final String UTF_8 = StandardCharsets.UTF_8.name();
+
+       private static final Map<Library, NativeLibrary> NATIVE_LIBRARY = Collections.synchronizedMap(new WeakHashMap<Library, NativeLibrary>());
+
+       protected final Pointer address;
+       
+       protected final String symbolName;
+
+       protected Constant(final NativeLibrary nativeLibrary, final String symbolName) {
+               this.symbolName = Objects.requireNonNull(symbolName);
+               address = nativeLibrary.getGlobalVariableAddress(symbolName);
+       }
+
+       protected static NativeLibrary getNativeLibrary(final Library library) {
+               return NATIVE_LIBRARY.computeIfAbsent(library, key -> ((Handler)Proxy.getInvocationHandler(key)).getNativeLibrary());
+       }
+
+       public static UInt16Const ofUInt16(final Library library, final String symbolName) {
+               return new UInt16Const(getNativeLibrary(library), symbolName);
+       }
+
+       public static StrPtrConst ofStrPtr(final Library library, final String symbolName) {
+               return new StrPtrConst(getNativeLibrary(library), symbolName);
+       }
+
+       public static class UInt16Const extends Constant {
+
+               protected UInt16Const(final NativeLibrary nativeLibrary, final String symbolName) {
+                       super(nativeLibrary, symbolName);
+               }
+
+               public UInt16 getValue() {
+                       return UInt16.of(address.getShort(0L));
+               }
+       }
+
+       public static class StrPtrConst extends Constant {
+
+               protected StrPtrConst(final NativeLibrary nativeLibrary, final String symbolName) {
+                       super(nativeLibrary, symbolName);
+               }
+
+               public String getValue() {
+                       return address.getPointer(0L).getString(0L, UTF_8);
+               }
+       }
+
+       public String getSymbolName() {
+               return symbolName;
+       }
+}
diff --git a/binding/java/src/overview.html b/binding/java/src/overview.html
new file mode 100644 (file)
index 0000000..0a83d1f
--- /dev/null
@@ -0,0 +1,27 @@
+<html>
+       <body>
+               <h1>SlunkCrypt Java Wrapper</h1>
+               <p>This package exposes the functionality of the <a href="https://gitlab.com/lord_mulder/slunkcrypt" target="_blank"><b>SlunkCrypt</b></a> library to to Java developers.</p>
+               <p>Please see the {@see com.muldersoft.slunkcrypt.SlunkCrypt} class for details!</p>
+               <h2>Dependencies</h2>
+               The SlunkCrypt Java wrapper requires <a href="https://mvnrepository.com/artifact/net.java.dev.jna/jna" target="_blank">Java Native Access (JNA)</a>, version 5.14.x or later, as a runtime dependency.
+               <h2>Build Instructions</h2>
+               <p>First the "native" SlunkCrypt library needs to be built, if not done yet:</p>
+               <pre>$ cd /home/JohnnyBeSlunk/dev/SlunkCrypt
+$ make -B SHARED=1</pre>
+               <p>Now build the SlunkCrypt Java wrapper package:</p>
+               <pre>$ cd binding/java
+$ ant</pre>
+               <h3>Prerequisites</h3>
+               <p><a href="https://ant.apache.org/bindownload.cgi">Apache Ant</a> and <a href="https://ant.apache.org/ivy/download.cgi" target="_blank">Apache Ivy</a> are required for buildig the SlunkCrypt Java wrapper from the sources using the provided <tt>build.xml</tt> file.</p>
+               <p><i>Note:</i> The file <tt>ivy.jar</tt> should be located in the <tt>ANT_HOME/lib</tt> directory.</p>
+               <p>
+               <h2>Unit Tests</h2>
+               In order to run the unit tests, please type:
+               <pre>$ export ANT_OPTS=-Djna.library.path=$PWD/../../libslunkcrypt/lib
+$ ant test</pre>
+               <h3>Prerequisites</h3>
+               <p>The <a href="https://mvnrepository.com/artifact/org.apache.ant/ant-junitlauncher">JUnitLauncher</a> (JUnit 5) is required to run the unit tests. All other required JUnit 5 libraries will be fetched automatically by Ivy.</p>
+               <p><i>Note:</i> The file <tt>ant-junitlauncher.jar</tt> should be located in the <tt>ANT_HOME/lib</tt> directory.</p>
+       </body>
+</html>
diff --git a/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/ContextTest.java b/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/ContextTest.java
new file mode 100644 (file)
index 0000000..f29ce6c
--- /dev/null
@@ -0,0 +1,248 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.tests;
+
+import static com.muldersoft.slunkcrypt.tests.Utilities.toHexString;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import com.muldersoft.slunkcrypt.SlunkCryptDecryptor;
+import com.muldersoft.slunkcrypt.SlunkCryptEncryptor;
+
+@TestMethodOrder(OrderAnnotation.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class ContextTest {
+
+       private static final String PASSPHRASE = "OrpheanBeholderScryDoubt";
+
+       private String lastTestName = new String();
+
+       @BeforeEach
+       void init(final TestInfo testInfo) {
+               final String name = testInfo.getTestMethod().map(method -> method.getName()).orElse("N/A");
+               if (!name.equalsIgnoreCase(lastTestName)) {
+                       System.out.printf("-------- [ %s ] --------%n", lastTestName = name);
+               } else {
+                       System.out.println("----");
+               }
+       }
+
+       @RepeatedTest(8)
+       @Order(1)
+       void createInstanceTest() throws Exception {
+               try(final SlunkCryptEncryptor encryptor = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       assertNotNull(encryptor);
+                       System.out.printf("EncryptContext: %s%n", encryptor);
+                       try(final SlunkCryptDecryptor decryptor = new SlunkCryptDecryptor(PASSPHRASE, encryptor.getNonce())) {
+                               assertNotNull(decryptor);
+                               System.out.printf("DecryptContext: %s%n", decryptor);
+                       }
+               }
+       }
+
+       @RepeatedTest(8)
+       @Order(2)
+       void processTest() throws Exception {
+               final long nonce;
+               final byte[] plaintext = new byte[256], encrypted = new byte[256], decrypted = new byte[256];
+               ThreadLocalRandom.current().nextBytes(plaintext);
+
+               System.out.printf("Plaintext: %s%n", toHexString(plaintext));
+
+               try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       nonce = instance.getNonce();
+                       instance.process(plaintext, encrypted);
+               }
+
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted));
+               assertFalse(Arrays.equals(encrypted, plaintext));
+
+               try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE, nonce)) {
+                       instance.process(encrypted, decrypted);
+               }
+
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted));
+               assertTrue(Arrays.equals(decrypted, plaintext));
+       }
+
+       @RepeatedTest(8)
+       @Order(3)
+       void processTest2() throws Exception {
+               final long nonce;
+               final byte[] plaintext = new byte[256], encrypted, decrypted;
+               ThreadLocalRandom.current().nextBytes(plaintext);
+
+               System.out.printf("Plaintext: %s%n", toHexString(plaintext));
+
+               try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       nonce = instance.getNonce();
+                       encrypted = instance.process(plaintext);
+               }
+
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted));
+               assertFalse(Arrays.equals(encrypted, plaintext));
+
+               try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE, nonce)) {
+                       decrypted = instance.process(encrypted);
+               }
+
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted));
+               assertTrue(Arrays.equals(decrypted, plaintext));
+       }
+
+       @RepeatedTest(8)
+       @Order(4)
+       void inplaceTest() throws Exception {
+               final long nonce;
+               final byte[] plaintext = new byte[256], encrypted = new byte[256], decrypted = new byte[256];
+               ThreadLocalRandom.current().nextBytes(plaintext);
+
+               System.out.printf("Plaintext: %s%n", toHexString(plaintext));
+
+               try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       nonce = instance.getNonce();
+                       System.arraycopy(plaintext, 0, encrypted, 0, plaintext.length);
+                       instance.inplace(encrypted);
+               }
+
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted));
+               assertFalse(Arrays.equals(encrypted, plaintext));
+
+               try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE, nonce)) {
+                       System.arraycopy(encrypted, 0, decrypted, 0, encrypted.length);
+                       instance.inplace(decrypted);
+               }
+
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted));
+               assertTrue(Arrays.equals(decrypted, plaintext));
+       }
+       
+       @RepeatedTest(8)
+       @Order(5)
+       void resetTest() throws Exception {
+               final long nonce_1, nonce_2;
+               final byte[] plaintext = new byte[256], encrypted_1 = new byte[256], encrypted_2 = new byte[256], decrypted_1 = new byte[256], decrypted_2 = new byte[256];
+               ThreadLocalRandom.current().nextBytes(plaintext);
+
+               System.out.printf("Plaintext: %s%n", toHexString(plaintext));
+
+               try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       nonce_1 = instance.getNonce();
+                       System.arraycopy(plaintext, 0, encrypted_1, 0, plaintext.length);
+                       instance.inplace(encrypted_1);
+                       instance.reset(PASSPHRASE);
+                       nonce_2 = instance.getNonce();
+                       System.arraycopy(plaintext, 0, encrypted_2, 0, plaintext.length);
+                       instance.inplace(encrypted_2);
+               }
+
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted_1));
+               assertFalse(Arrays.equals(encrypted_1, plaintext));
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted_2));
+               assertFalse(Arrays.equals(encrypted_2, plaintext));
+
+               try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE, nonce_1)) {
+                       System.arraycopy(encrypted_1, 0, decrypted_1, 0, encrypted_1.length);
+                       instance.inplace(decrypted_1);
+                       instance.reset(PASSPHRASE, nonce_2);
+                       System.arraycopy(encrypted_2, 0, decrypted_2, 0, encrypted_2.length);
+                       instance.inplace(decrypted_2);
+               }
+
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted_1));
+               assertTrue(Arrays.equals(decrypted_1, plaintext));
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted_2));
+               assertTrue(Arrays.equals(decrypted_2, plaintext));
+       }
+
+       @RepeatedTest(8)
+       @Order(6)
+       void failBadNonceTest() throws Exception {
+               final long nonce;
+               final byte[] plaintext = new byte[256], encrypted = new byte[256], decrypted = new byte[256];
+               ThreadLocalRandom.current().nextBytes(plaintext);
+
+               System.out.printf("Plaintext: %s%n", toHexString(plaintext));
+
+               try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       nonce = instance.getNonce();
+                       System.arraycopy(plaintext, 0, encrypted, 0, plaintext.length);
+                       instance.inplace(encrypted);
+               }
+
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted));
+               assertFalse(Arrays.equals(encrypted, plaintext));
+
+               try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE, nonce + 1)) {
+                       System.arraycopy(encrypted, 0, decrypted, 0, encrypted.length);
+                       instance.inplace(decrypted);
+               }
+
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted));
+               assertFalse(Arrays.equals(decrypted, plaintext));
+       }
+
+       @RepeatedTest(8)
+       @Order(7)
+       void failBadPasswdTest() throws Exception {
+               final long nonce;
+               final byte[] plaintext = new byte[256], encrypted = new byte[256], decrypted = new byte[256];
+               ThreadLocalRandom.current().nextBytes(plaintext);
+               System.out.printf("Plaintext: %s%n", toHexString(plaintext));
+
+               try(final SlunkCryptEncryptor instance = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       nonce = instance.getNonce();
+                       System.arraycopy(plaintext, 0, encrypted, 0, plaintext.length);
+                       instance.inplace(encrypted);
+               }
+
+               System.out.printf("Encrypted: %s%n", toHexString(encrypted));
+               assertFalse(Arrays.equals(encrypted, plaintext));
+
+               try(final SlunkCryptDecryptor instance = new SlunkCryptDecryptor(PASSPHRASE.replace('O', '0'), nonce)) {
+                       System.arraycopy(encrypted, 0, decrypted, 0, encrypted.length);
+                       instance.inplace(decrypted);
+               }
+
+               System.out.printf("Decrypted: %s%n", toHexString(decrypted));
+               assertFalse(Arrays.equals(decrypted, plaintext));
+       }
+
+       @Test
+       @Order(8)
+       void stressTest() throws Exception {
+               try(final SlunkCryptEncryptor encryptor = new SlunkCryptEncryptor(PASSPHRASE)) {
+                       try(final SlunkCryptDecryptor decryptor = new SlunkCryptDecryptor(PASSPHRASE, encryptor.getNonce())) {
+                               final byte[] plaintext = new byte[0xFFFF], processed = new byte[0xFFFF];
+                               for (int i = 0; i < 0xFFFF; ++i) {
+                                       ThreadLocalRandom.current().nextBytes(plaintext);
+                                       System.arraycopy(plaintext, 0, processed, 0, plaintext.length);
+                                       encryptor.inplace(processed);
+                                       assertFalse(Arrays.equals(processed, plaintext));
+                                       decryptor.inplace(processed);
+                                       assertTrue(Arrays.equals(processed, plaintext));
+                                       if (i % 499 == 0) {
+                                               System.out.printf("%.1f%%%n", (i / (double)0xFFFF) * 100.0);
+                                       }
+                               }
+                       }
+                       System.out.printf("%5.1f%%%n", 100.0);
+               }
+       }
+}
diff --git a/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/FunctionTest.java b/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/FunctionTest.java
new file mode 100644 (file)
index 0000000..2f18012
--- /dev/null
@@ -0,0 +1,74 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
+/* This work has been released under the CC0 1.0 Universal license!           */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt.tests;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import com.muldersoft.slunkcrypt.SlunkCrypt;
+import com.muldersoft.slunkcrypt.SlunkCryptException;
+
+@TestMethodOrder(OrderAnnotation.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class FunctionTest {
+
+       private String lastTestName = new String();
+
+       @BeforeEach
+       void init(final TestInfo testInfo) {
+               final String name = testInfo.getTestMethod().map(method -> method.getName()).orElse("N/A");
+               if (!name.equalsIgnoreCase(lastTestName)) {
+                       System.out.printf("-------- [ %s ] --------%n", lastTestName = name);
+               } else {
+                       System.out.println("----");
+               }
+       }
+
+       @Test
+       @Order(1)
+       void getVersionTest() {
+               final Map<String, Short> versionInfo = SlunkCrypt.getVersion();
+               assertNotNull(versionInfo);
+               final Short major, minor, patch;
+               assertNotNull(major = versionInfo.get("slunkcrypt.version.major"));
+               assertNotNull(minor = versionInfo.get("slunkcrypt.version.minor"));
+               assertNotNull(patch = versionInfo.get("slunkcrypt.version.patch"));
+               System.out.printf("Version: %d.%d.%d%n", major.shortValue(), minor.shortValue(), patch.shortValue());
+       }
+
+       @Test
+       @Order(2)
+       void getBuildTest() {
+               final String build = SlunkCrypt.getBuild();
+               assertNotNull(build);
+               assertFalse(build.isEmpty());
+               System.out.printf("Build: \"%s\"%n", build);
+       }
+
+       @RepeatedTest(8)
+       @Order(3)
+       void generateNonceTest() throws SlunkCryptException {
+               final long nonce_1 = SlunkCrypt.generateNonce();
+               System.out.printf("Nonce: %016X%n", nonce_1);
+               
+               final long nonce_2 = SlunkCrypt.generateNonce();
+               System.out.printf("Nonce: %016X%n", nonce_2);
+               
+               assertNotEquals(nonce_1, nonce_2);
+       }
+}
diff --git a/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/Utilities.java b/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/Utilities.java
new file mode 100644 (file)
index 0000000..dec7b66
--- /dev/null
@@ -0,0 +1,16 @@
+package com.muldersoft.slunkcrypt.tests;
+
+public class Utilities {
+
+       private Utilities() {
+               throw new IllegalAccessError();
+       }
+
+       public static String toHexString(final byte[] data) {
+               final StringBuilder sb = new StringBuilder(Math.multiplyExact(2, data.length));
+               for (final byte b : data) {
+                       sb.append(String.format("%02X", b));
+               }
+               return sb.toString();
+       }
+}