*/
+/* 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.
+ * Implementations of this interface wrap the “native” SlunkCrypt context and they provide the instance methods for {@link SlunkCrypt#process(byte[]) processing}, i.e. encrypting or decrypting, chunks of data.
+ * Separate implementations for {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor encryption} mode and {@link com.muldersoft.slunkcrypt.SlunkCryptDecryptor decryption} mode are available.
+ * Warning: The wrapped “native” SlunkCrypt context must be released by calling the {@link SlunkCrypt#close close()} method, or otherwise a resource leak is imminent! Using try-with-resources is recommended.
+ * System properties:
+ *
+ *
+ * Property |
+ * Meaning |
+ *
+ *
+ * slunkcrypt.thread_count |
+ * The number of threads to be used internally by SlunkCrypt; a value of 0 means auto-detection (the default) |
+ *
+ *
+ * Thread Safety
+ * Separate SlunkCrypt instances can safely be accessed from concurrent threads without 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 share the same SlunkCrypt instance between concurrent threads, every access to the “shared” instance must be synchronized explicitly by the callers!
+ * Example
+ * {@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);
+ * }
+ * }
+ * }}
+ */
+public interface SlunkCrypt extends AutoCloseable {
+
+ /**
+ * Process, i.e. encrypt or decrypt, the next chunk of data.
+ * This method is designed for stream processing. It is supposed to be called repeatedly, until all input data has been processed.
+ * @param input the input buffer to be read; will not 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.e. encrypt or decrypt, the next chunk of data.
+ * This method is designed for stream processing. It is supposed to be called repeatedly, until all input data has been processed.
+ * Note: The size of the output buffer must be at least the size of the input buffer!
+ * @param input the input buffer to be read; will not 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.e. encrypt or decrypt, the next chunk of data in-place.
+ * This method is designed for stream processing. It is supposed to be called repeatedly, until all input data has been processed.
+ * @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.
+ * Note: The nonce is generating using a “strong” cryptographically secure pseudorandom number generator.
+ * @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 “native” SlunkCrypt library.
+ * @return The version number as Map<String, Short>, containing the keys: - "slunkcrypt.version.major",
- "slunkcrypt.version.minor",
- "slunkcrypt.version.patch"
+ */
+ public static Map getVersion() {
+ final Map version = new LinkedHashMap(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 “native” SlunkCrypt library.
+ * @return The build string, containing the build time and date, e.g. "Jun 24 2024, 21:55:08"
+ */
+ public static String getBuild() {
+ return SlunkCryptLibrary.Version.BUILD.getValue();
+ }
+
+ /**
+ * Close the “native” SlunkCrypt context wrapped by this {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance and release any system resources associated with it.
+ * Warning: Once this method has been called, this instance is left in an “invalidated” state and any attempt to process more data is going to throw an IllegalStateException!
+ */
+ @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
index 0000000..fedc19d
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptDecryptor.java
@@ -0,0 +1,59 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* This work has been released under the CC0 1.0 Universal license! */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+/**
+ * SlunkCrypt decryption context.
+ * Wraps a “native” SlunkCrypt context that was initialized in decryption mode and implements the {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} interface.
+ */
+public class SlunkCryptDecryptor extends AbstractSlunkCrypt {
+
+ /**
+ * Create a new {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance in decryption 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 decryption 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
index 0000000..a08c3bc
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptEncryptor.java
@@ -0,0 +1,79 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* This work has been released under the CC0 1.0 Universal license! */
+/******************************************************************************/
+
+package com.muldersoft.slunkcrypt;
+
+/**
+ * SlunkCrypt encryption context.
+ * Wraps a “native” SlunkCrypt context that was initialized in encryption mode and implements the {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} interface.
+ */
+public class SlunkCryptEncryptor extends AbstractSlunkCrypt {
+
+ private long nonce;
+
+ /**
+ * Create a new {@link com.muldersoft.slunkcrypt.SlunkCrypt SlunkCrypt} instance in encryption mode.
+ * Note: This method implicitly generates a new random nonce. Use {@link SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.
+ * @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 encryption mode.
+ * Note: This method implicitly generates a new random nonce. Use {@link SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.
+ * @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.
+ * Note: This method implicitly generates a new random nonce. Use {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.
+ * @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.
+ * Note: This method implicitly generates a new random nonce. Use {@link com.muldersoft.slunkcrypt.SlunkCryptEncryptor#getNonce() getNonce()} to retrieve the nonce value.
+ * @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
index 0000000..ce72471
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/SlunkCryptException.java
@@ -0,0 +1,68 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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.
+ * Details are provided via {@link ErrorKind}.
+ */
+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
index 0000000..87e2ba6
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptLibrary.java
@@ -0,0 +1,35 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..a7c7a7d
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/SlunkCryptParam.java
@@ -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
index 0000000..2a81165
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/Context.java
@@ -0,0 +1,29 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..dc959d7
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/SizeT.java
@@ -0,0 +1,26 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..e57316e
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt16.java
@@ -0,0 +1,25 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..6924f6d
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64.java
@@ -0,0 +1,25 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..d617c68
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/types/UInt64ByReference.java
@@ -0,0 +1,32 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..9a6648a
--- /dev/null
+++ b/binding/java/src/com/muldersoft/slunkcrypt/internal/utilities/Constant.java
@@ -0,0 +1,73 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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 NATIVE_LIBRARY = Collections.synchronizedMap(new WeakHashMap());
+
+ 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
index 0000000..0a83d1f
--- /dev/null
+++ b/binding/java/src/overview.html
@@ -0,0 +1,27 @@
+
+
+ SlunkCrypt Java Wrapper
+ This package exposes the functionality of the SlunkCrypt library to to Java developers.
+ Please see the {@see com.muldersoft.slunkcrypt.SlunkCrypt} class for details!
+ Dependencies
+ The SlunkCrypt Java wrapper requires Java Native Access (JNA), version 5.14.x or later, as a runtime dependency.
+ Build Instructions
+ First the "native" SlunkCrypt library needs to be built, if not done yet:
+ $ cd /home/JohnnyBeSlunk/dev/SlunkCrypt
+$ make -B SHARED=1
+ Now build the SlunkCrypt Java wrapper package:
+ $ cd binding/java
+$ ant
+ Prerequisites
+ Apache Ant and Apache Ivy are required for buildig the SlunkCrypt Java wrapper from the sources using the provided build.xml file.
+ Note: The file ivy.jar should be located in the ANT_HOME/lib directory.
+
+
Unit Tests
+ In order to run the unit tests, please type:
+ $ export ANT_OPTS=-Djna.library.path=$PWD/../../libslunkcrypt/lib
+$ ant test
+ Prerequisites
+ The JUnitLauncher (JUnit 5) is required to run the unit tests. All other required JUnit 5 libraries will be fetched automatically by Ivy.
+ Note: The file ant-junitlauncher.jar should be located in the ANT_HOME/lib directory.
+
+
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
index 0000000..f29ce6c
--- /dev/null
+++ b/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/ContextTest.java
@@ -0,0 +1,248 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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
index 0000000..2f18012
--- /dev/null
+++ b/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/FunctionTest.java
@@ -0,0 +1,74 @@
+/******************************************************************************/
+/* SlunkCrypt, by LoRd_MuldeR */
+/* 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 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
index 0000000..dec7b66
--- /dev/null
+++ b/binding/java/tests/src/com/muldersoft/slunkcrypt/tests/Utilities.java
@@ -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();
+ }
+}