Compare commits

...

11 Commits
1.3.1 ... MT

60 changed files with 2257 additions and 91 deletions

8
.gitignore vendored
View File

@ -1,7 +1,11 @@
*.html *.html
*.user *.user
/**/.settings
/**/.vs
/**/bin /**/bin
/**/dist
/**/doc
/**/lib /**/lib
/**/obj /**/obj
/.vs /**/out
/out /**/target

View File

@ -6,13 +6,20 @@ DEBUG ?= 0
NALYZE ?= 0 NALYZE ?= 0
ASAN ?= 0 ASAN ?= 0
STATIC ?= 0 STATIC ?= 0
SHARED ?= 0
FLTO ?= 0 FLTO ?= 0
FPGO ?= 0 FPGO ?= 0
STRIP ?= 0 STRIP ?= 0
CPU ?= 0 CPU ?= 0
THREAD ?= 1 THREAD ?= 1
$(info Options: DEBUG=$(DEBUG), NALYZE=$(NALYZE), ASAN=$(ASAN), STATIC=$(STATIC), FLTO=$(FLTO), FPGO=$(FPGO), STRIP=$(STRIP), CPU=$(CPU), THREAD=$(THREAD)) $(info Options: DEBUG=$(DEBUG), NALYZE=$(NALYZE), ASAN=$(ASAN), STATIC=$(STATIC), SHARED=$(SHARED), FLTO=$(FLTO), FPGO=$(FPGO), STRIP=$(STRIP), CPU=$(CPU), THREAD=$(THREAD))
ifneq ($(SHARED),0)
ifneq ($(STATIC),0)
$(error Options SHARED=1 and STATIC=1 are mutually exclusive!)
endif
endif
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Directories # Directories
@ -72,6 +79,9 @@ endif
ifneq (,$(firstword $(filter %mingw32 %-windows-gnu %-cygwin %-cygnus,$(MACHINE)))) ifneq (,$(firstword $(filter %mingw32 %-windows-gnu %-cygwin %-cygnus,$(MACHINE))))
EXE_SUFFIX := .exe EXE_SUFFIX := .exe
LIB_SUFFIX := .$(if $(subst 0,,$(SHARED)),dll,a)
else
LIB_SUFFIX := .$(if $(subst 0,,$(SHARED)),$(if $(findstring -apple-darwin,$(MACHINE)),dylib,so),a)
endif endif
ifneq (,$(firstword $(filter %-w64-mingw32 %w64-windows-gnu,$(MACHINE)))) ifneq (,$(firstword $(filter %-w64-mingw32 %w64-windows-gnu,$(MACHINE))))
@ -96,12 +106,26 @@ ifneq (,$(firstword $(filter %-pc-haiku %-unknown-haiku,$(MACHINE))))
LDFLGS += -lbsd LDFLGS += -lbsd
endif endif
APP_CFLAGS = $(CFLAGS)
APP_LDFLGS = -L$(SUBDIR_LIB)/lib -l$(VERSION_LIB) $(LDFLGS)
LIB_CFLAGS = $(CFLAGS)
LIB_LDFLGS = $(LDFLGS)
ifneq ($(SHARED),0)
LIB_CFLAGS += -fPIC
ifneq (,$(firstword $(filter %-w64-mingw32 %w64-windows-gnu,$(MACHINE))))
LIB_LDFLGS = -Wl,--out-implib,"$@.a" $(LDFLGS)
endif
endif
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# File names # File names
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
VERSION_LIB := slunkcrypt$(CONFIG)-1
OUTNAME_APP := slunkcrypt$(CONFIG)$(EXE_SUFFIX) OUTNAME_APP := slunkcrypt$(CONFIG)$(EXE_SUFFIX)
OUTNAME_LIB := libslunkcrypt$(CONFIG)-1.a OUTNAME_LIB := lib$(VERSION_LIB)$(LIB_SUFFIX)
OUTPATH_APP := $(SUBDIR_APP)/bin/$(OUTNAME_APP) OUTPATH_APP := $(SUBDIR_APP)/bin/$(OUTNAME_APP)
OUTPATH_LIB := $(SUBDIR_LIB)/lib/$(OUTNAME_LIB) OUTPATH_LIB := $(SUBDIR_LIB)/lib/$(OUTNAME_LIB)
@ -121,21 +145,27 @@ endif
# Targets # Targets
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
.PHONY: all clean .PHONY: all build clean
all: $(OUTPATH_APP) all: clean build
build: $(OUTPATH_APP)
$(OUTPATH_APP): $(OBJECTS_APP) $(OUTPATH_LIB) $(OUTPATH_APP): $(OBJECTS_APP) $(OUTPATH_LIB)
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) $(CFLAGS) $^ -o $@ $(LDFLGS) $(CC) $(APP_CFLAGS) $(OBJECTS_APP) -o $@ $(APP_LDFLGS)
$(OUTPATH_LIB): $(OBJECTS_LIB) $(OUTPATH_LIB): $(OBJECTS_LIB)
@mkdir -p $(@D) @mkdir -p $(@D)
ifneq ($(SHARED),0)
$(CC) $(LIB_CFLAGS) $^ -shared -o $@ $(LIB_LDFLGS)
else
$(AR) rcs $@ $^ $(AR) rcs $@ $^
endif
$(SUBDIR_APP)/obj/%$(CONFIG).o: $(SUBDIR_APP)/src/%.c $(SUBDIR_APP)/obj/%$(CONFIG).o: $(SUBDIR_APP)/src/%.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(APP_CFLAGS) -c $< -o $@
$(SUBDIR_APP)/obj/%.rsrc.o: $(SUBDIR_APP)/res/%.rc $(SUBDIR_APP)/obj/%.rsrc.o: $(SUBDIR_APP)/res/%.rc
@mkdir -p $(@D) @mkdir -p $(@D)
@ -143,8 +173,8 @@ $(SUBDIR_APP)/obj/%.rsrc.o: $(SUBDIR_APP)/res/%.rc
$(SUBDIR_LIB)/obj/%$(CONFIG).o: $(SUBDIR_LIB)/src/%.c $(SUBDIR_LIB)/obj/%$(CONFIG).o: $(SUBDIR_LIB)/src/%.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(LIB_CFLAGS) -c $< -o $@
clean: clean:
$(RM) $(SUBDIR_APP)/obj/*.o $(SUBDIR_APP)/obj/*.gcda $(SUBDIR_APP)/lib/*.a $(SUBDIR_APP)/bin/*$(EXE_SUFFIX) $(RM) $(SUBDIR_APP)/obj/* $(SUBDIR_APP)/lib/* $(SUBDIR_APP)/bin/*
$(RM) $(SUBDIR_LIB)/obj/*.o $(SUBDIR_LIB)/obj/*.gcda $(SUBDIR_LIB)/lib/*.a $(SUBDIR_LIB)/bin/*$(EXE_SUFFIX) $(RM) $(SUBDIR_LIB)/obj/* $(SUBDIR_LIB)/lib/* $(SUBDIR_LIB)/bin/*

View File

@ -63,6 +63,9 @@ The following settings can be adjusted in the `slunkcrypt-gui.exe.config` config
- **`KeepIncompleteFiles`:** - **`KeepIncompleteFiles`:**
If set to `true`, incomplete or corrupted output files will *not* be deleted &mdash; default value: `false`. If set to `true`, incomplete or corrupted output files will *not* be deleted &mdash; default value: `false`.
- **`LegacyCompat`:**
If set to `1`, show options to enable “legacy” compatibility-mode in the GUI; if set to `2`, select “legacy” compatibility-mode by default &mdash; default value: `0`.
Command-line Usage Command-line Usage
================== ==================
@ -132,7 +135,7 @@ The following environment variables may be used:
Specifies the number of worker threads to use. By default, SlunkCrypt detects the number of available processors and creates one thread for each processor. Specifies the number of worker threads to use. By default, SlunkCrypt detects the number of available processors and creates one thread for each processor.
- **`SLUNK_LEGACY_COMPAT`**: - **`SLUNK_LEGACY_COMPAT`**:
If set to a *non-zero* value, enables "legacy" compatibility-mode, required to decrypt files encrypted with SlunkCrypt version 1.2.x or older. If set to a *non-zero* value, enables “legacy” compatibility-mode, required to decrypt files encrypted with SlunkCrypt version 1.2.x or older.
- **`SLUNK_DEBUG_LOGGING`**: - **`SLUNK_DEBUG_LOGGING`**:
If set to a *non-zero* value, enables additional logging output to the syslog (Unix-like) or to the debugger (Windows). This is intended for debugging purposes only! If set to a *non-zero* value, enables additional logging output to the syslog (Unix-like) or to the debugger (Windows). This is intended for debugging purposes only!

17
binding/java/.classpath Normal file
View File

@ -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>

18
binding/java/.project Normal file
View File

@ -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>

73
binding/java/build.xml Normal file
View File

@ -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>

14
binding/java/ivy.xml Normal file
View File

@ -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>

View File

@ -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!");
}
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

101
binding/rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,101 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "memx"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93022fcc0eab2dc9dd134e362592e06f0b3c0aa549ae91d8b3e539e56c2a0687"
dependencies = [
"cpufeatures",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "slunkcrypt-rs"
version = "1.0.0"
dependencies = [
"hex",
"memx",
"rand",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

9
binding/rust/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "slunkcrypt-rs"
version = "1.0.0"
edition = "2021"
[dev-dependencies]
hex = "0.4.3"
memx = "0.1.32"
rand = "0.8.5"

11
binding/rust/build.rs Normal file
View File

@ -0,0 +1,11 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use std::env;
fn main() {
println!(r"cargo:rustc-link-search={}/../../libslunkcrypt/lib", env::var("CARGO_MANIFEST_DIR").unwrap());
println!(r"cargo:rustc-link-lib=slunkcrypt-1");
}

View File

@ -0,0 +1,31 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use slunkcrypt_rs::{SlunkCrypt, SlunkCryptPasswd};
use hex;
use rand::{thread_rng, RngCore};
use std::mem;
fn main() {
// Create passphrase from string
const PASSPHRASE: SlunkCryptPasswd = SlunkCryptPasswd::Str("OrpheanBeholderScryDoubt");
// Fill buffer with random data (plaintext)
let mut buffer = [ 0u8; 64 ];
thread_rng().fill_bytes(&mut buffer);
println!("Plaintext: {}", hex::encode(&buffer));
// Encrypt the data in-place
let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
context_enc.inplace(&mut buffer).expect("Failed to encrypt data!");
mem::drop(context_enc);
println!("Encrypted: {}", hex::encode(&buffer));
// Decrypt the data in-place
let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!");
context_dec.inplace(&mut buffer).expect("Failed to decrypt data!");
mem::drop(context_dec);
println!("Decrypted: {}", hex::encode(&buffer));
}

185
binding/rust/src/context.rs Normal file
View File

@ -0,0 +1,185 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use std::num::NonZeroUsize;
use std::ptr;
use crate::error::{SlunkCryptError, SlunkCryptErrorKind};
use crate::functions::generate_nonce;
use crate::ffi::libslunkcrypt::{self, slunkcrypt_t, slunkparam_t, SLUNKCRYPT_SUCCESS, SLUNKCRYPT_NULL};
use crate::passwd::SlunkCryptPasswd;
/// SlunkCrypt context for encryption or decryption.
///
/// This struct wraps the "native" SlunkCrypt context. It provides functions for allocating a new encryption or decryption context as well as functions for processing, i.e. encrypting or decrypting, the next chunk of data within a specific `SlunkCrypt` context.
///
/// The wrapped "native" SlunkCrypt context is freed automatically when this struct is dropped.
///
/// # Thread safety
///
/// Separate `SlunkCrypt` contexts can safely be accessed from concurrent threads *without* the need for any kind if synchronization, provided that each context is only accessed by the *single* thread that has allocated the respective context.
///
/// # Example
/// ```
/// use slunkcrypt_rs::{SlunkCrypt, SlunkCryptPasswd};
/// use hex;
/// use rand::{thread_rng, RngCore};
/// use std::mem;
///
/// fn main() {
/// // Create passphrase from string
/// const PASSPHRASE: SlunkCryptPasswd = SlunkCryptPasswd::Str("OrpheanBeholderScryDoubt");
///
/// // Fill buffer with random data (plaintext)
/// let mut buffer = [ 0u8; 64 ];
/// thread_rng().fill_bytes(&mut buffer);
/// println!("Plaintext: {}", hex::encode(&buffer));
///
/// // Encrypt the data in-place
/// let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).unwrap();
/// context_enc.inplace(&mut buffer).unwrap();
/// mem::drop(context_enc);
/// println!("Encrypted: {}", hex::encode(&buffer));
///
/// // Decrypt the data in-place
/// let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).unwrap();
/// context_dec.inplace(&mut buffer).unwrap();
/// mem::drop(context_dec);
/// println!("Decrypted: {}", hex::encode(&buffer));
/// }
/// ```
#[derive(Debug)]
pub struct SlunkCrypt {
context: slunkcrypt_t
}
impl SlunkCrypt {
/// Create a new SlunkCrypt context and initialize it for the specified mode with the given password and the given nonce.
///
/// If successful, returns the new context; otherwise returns an [error code](SlunkCryptError).
fn new(passwd: &SlunkCryptPasswd, nonce: u64, mode: i32, threads: Option<NonZeroUsize>) -> Result<Self, SlunkCryptError> {
if passwd.len() < libslunkcrypt::SLUNKCRYPT_PWDLEN_MIN {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too short!"));
}
if passwd.len() > libslunkcrypt::SLUNKCRYPT_PWDLEN_MAX {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too long!"));
}
let param = slunkparam_t {
version: libslunkcrypt::SLUNKCRYPT_PARAM_VERSION,
thread_count: threads.map_or(usize::default(), NonZeroUsize::get),
legacy_compat: i32::default(),
debug_logging: i32::default()
};
let context: slunkcrypt_t = unsafe {
libslunkcrypt::slunkcrypt_alloc_ext(nonce, passwd.as_bytes().as_ptr() as *const u8, passwd.len(), mode, &param)
};
if context == SLUNKCRYPT_NULL {
Err(SlunkCryptError::new(SlunkCryptErrorKind::Failure, "failed to allocate slunkcrypt encryption context!"))
} else {
Ok(Self { context })
}
}
/// Create a new SlunkCrypt context and initialize it for *encryption* mode with the given password and a fresh random nonce.
///
/// If successful, returns the new context and the generated nonce; otherwise returns an [error code](SlunkCryptError).
pub fn init_encrypt(passwd: &SlunkCryptPasswd, threads: Option<NonZeroUsize>) -> Result<(Self, u64), SlunkCryptError> {
let nonce = generate_nonce()?;
match Self::new(passwd, nonce, libslunkcrypt::SLUNKCRYPT_ENCRYPT, threads) {
Ok(context) => Ok((context, nonce)),
Err(error) => Err(error)
}
}
/// Create a new SlunkCrypt context and initialize it for *decryption* mode with the given password and the given nonce.
///
/// If successful, returns the new context; otherwise returns an [error code](SlunkCryptError).
pub fn init_decrypt(passwd: &SlunkCryptPasswd, nonce: u64, threads: Option<NonZeroUsize>) -> Result<Self, SlunkCryptError> {
Self::new(passwd, nonce, libslunkcrypt::SLUNKCRYPT_DECRYPT, threads)
}
pub fn reset_encrypt(&mut self, passwd: &SlunkCryptPasswd) -> Result<u64, SlunkCryptError> {
if passwd.len() < libslunkcrypt::SLUNKCRYPT_PWDLEN_MIN {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too short!"));
}
if passwd.len() > libslunkcrypt::SLUNKCRYPT_PWDLEN_MAX {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too long!"));
}
let nonce = generate_nonce()?;
let retval = unsafe {
libslunkcrypt::slunkcrypt_reset(self.context, nonce, passwd.as_bytes().as_ptr(), passwd.len(), libslunkcrypt::SLUNKCRYPT_ENCRYPT)
};
match retval {
SLUNKCRYPT_SUCCESS => Ok(nonce),
_ => Err(SlunkCryptError::from_retval(retval, "failed to reset encryption context!"))
}
}
pub fn reset_decrypt(&mut self, passwd: &SlunkCryptPasswd, nonce: u64) -> Result<(), SlunkCryptError> {
if passwd.len() < libslunkcrypt::SLUNKCRYPT_PWDLEN_MIN {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too short!"));
}
if passwd.len() > libslunkcrypt::SLUNKCRYPT_PWDLEN_MAX {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too long!"));
}
let retval = unsafe {
libslunkcrypt::slunkcrypt_reset(self.context, nonce, passwd.as_bytes().as_ptr(), passwd.len(), libslunkcrypt::SLUNKCRYPT_DECRYPT)
};
match retval {
SLUNKCRYPT_SUCCESS => Ok(()),
_ => Err(SlunkCryptError::from_retval(retval, "failed to reset decryption context!"))
}
}
/// Process the next chunk of data. The given input data is encrypted or decrypted into the output buffer.
///
/// Does **not** modify the data in the input buffer.
///
/// If successful, returns nothing; otherwise returns an [error code](SlunkCryptError).
pub fn process(&mut self, data_in: &[u8], data_out: &mut[u8]) -> Result<(), SlunkCryptError> {
if data_out.len() < data_in.len() {
return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "The output buffer is too small!"));
}
let retval = unsafe {
libslunkcrypt::slunkcrypt_process(self.context, data_in.as_ptr(), data_out.as_mut_ptr(), data_in.len())
};
match retval {
SLUNKCRYPT_SUCCESS => Ok(()),
_ => Err(SlunkCryptError::from_retval(retval, "failed to process data!"))
}
}
/// Process the next chunk of data. The given data is encrypted or decrypted *in-place*.
///
/// If successful, returns nothing; otherwise returns an [error code](SlunkCryptError).
pub fn inplace(&mut self, data: &mut[u8]) -> Result<(), SlunkCryptError> {
let retval = unsafe {
libslunkcrypt::slunkcrypt_inplace(self.context, data.as_mut_ptr(), data.len())
};
match retval {
SLUNKCRYPT_SUCCESS => Ok(()),
_ => Err(SlunkCryptError::from_retval(retval, "failed to process data!"))
}
}
}
impl Drop for SlunkCrypt {
/// Free the SlunkCrypt context and release all of its associated ressources.
fn drop(&mut self) {
if self.context != SLUNKCRYPT_NULL {
unsafe {
libslunkcrypt::slunkcrypt_free(self.context);
}
let x : *const std::ffi::c_void = ptr::null();
self.context = x as slunkcrypt_t;
}
}
}

61
binding/rust/src/error.rs Normal file
View File

@ -0,0 +1,61 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use std::os::raw::c_int;
use crate::ffi::libslunkcrypt::{SLUNKCRYPT_ABORTED, SLUNKCRYPT_FAILURE, SLUNKCRYPT_SUCCESS};
/// SlunkCrypt error type.
///
/// This struct provides information about the SlunkCrypt error that has occurred.
#[derive(Debug)]
pub struct SlunkCryptError {
kind: SlunkCryptErrorKind,
description: &'static str
}
/// SlunkCrypt error kind enumeration.
///
/// This enumeration defines the *kind* of the SlunkCrypt error that has occurred.
#[derive(Clone, Copy, Debug)]
pub enum SlunkCryptErrorKind {
/// The operation succeeded.
Success,
/// The operation was **not** executed because of invalid arguments.
Invalid,
/// The operation has failed.
Failure,
/// The operation was aborted by the user.
Aborted,
/// Unknown error. This is **not** supposed to happen.
Unknown
}
impl SlunkCryptError {
/// Creates a new error from the given kind and description.
pub(crate) fn new(kind: SlunkCryptErrorKind, description: &'static str) -> Self {
Self { kind, description }
}
/// Creates a new error from the given error code and description.
pub(crate) fn from_retval(retval: c_int, description: &'static str) -> Self {
let kind = match retval {
SLUNKCRYPT_SUCCESS => SlunkCryptErrorKind::Success,
SLUNKCRYPT_ABORTED => SlunkCryptErrorKind::Aborted,
SLUNKCRYPT_FAILURE => SlunkCryptErrorKind::Failure,
_ => SlunkCryptErrorKind::Unknown
};
Self::new(kind, description)
}
/// Returns the [*kind*](SlunkCryptErrorKind) of the SlunkCrypt error that has occurred.
pub fn kind(&self) -> SlunkCryptErrorKind {
self.kind
}
/// Returns a detailed textual description of the SlunkCrypt error that has occurred.
pub fn description(&self) -> &'static str {
&self.description
}
}

View File

@ -0,0 +1,50 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
pub type slunkcrypt_t = usize;
const NULL_POINTER: *const ::std::ffi::c_void = ::std::ptr::null();
pub const SLUNKCRYPT_NULL: slunkcrypt_t = unsafe { ::std::mem::transmute(NULL_POINTER) };
pub const SLUNKCRYPT_FALSE: ::std::os::raw::c_int = 0;
pub const SLUNKCRYPT_TRUE: ::std::os::raw::c_int = 1;
pub const SLUNKCRYPT_ENCRYPT: ::std::os::raw::c_int = 0;
pub const SLUNKCRYPT_DECRYPT: ::std::os::raw::c_int = 1;
pub const SLUNKCRYPT_SUCCESS: ::std::os::raw::c_int = 0;
pub const SLUNKCRYPT_FAILURE: ::std::os::raw::c_int = -1;
pub const SLUNKCRYPT_ABORTED: ::std::os::raw::c_int = -2;
pub const SLUNKCRYPT_PWDLEN_MIN: usize = 8;
pub const SLUNKCRYPT_PWDLEN_MAX: usize = 256;
pub const SLUNKCRYPT_PARAM_VERSION: u16 = 2;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct slunkparam_t {
pub version: u16,
pub thread_count: usize,
pub legacy_compat: ::std::os::raw::c_int,
pub debug_logging: ::std::os::raw::c_int,
}
extern "C" {
pub static SLUNKCRYPT_VERSION_MAJOR: u16;
pub static SLUNKCRYPT_VERSION_MINOR: u16;
pub static SLUNKCRYPT_VERSION_PATCH: u16;
pub static SLUNKCRYPT_BUILD: *const ::std::os::raw::c_char;
pub static SLUNKCRYPT_HAVE_THREADS: ::std::os::raw::c_int;
pub static mut g_slunkcrypt_abort_flag: ::std::os::raw::c_int;
pub fn slunkcrypt_generate_nonce(nonce: *mut u64) -> ::std::os::raw::c_int;
pub fn slunkcrypt_alloc_ext(nonce: u64, passwd: *const u8, passwd_len: usize, mode: ::std::os::raw::c_int, param: *const slunkparam_t) -> slunkcrypt_t;
pub fn slunkcrypt_reset(context: slunkcrypt_t, nonce: u64, passwd: *const u8, passwd_len: usize, mode: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
pub fn slunkcrypt_free(context: slunkcrypt_t);
pub fn slunkcrypt_process(context: slunkcrypt_t, input: *const u8, output: *mut u8, length: usize) -> ::std::os::raw::c_int;
pub fn slunkcrypt_inplace(context: slunkcrypt_t, buffer: *mut u8, length: usize) -> ::std::os::raw::c_int;
pub fn slunkcrypt_random_bytes(buffer: *mut u8, length: usize) -> usize;
pub fn slunkcrypt_bzero(buffer: *mut ::std::os::raw::c_void, length: usize);
}

View File

@ -0,0 +1,13 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#![allow(dead_code)]
#![allow(unused)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
pub mod libslunkcrypt;

View File

@ -0,0 +1,38 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use std::ffi::CStr;
use std::str;
use crate::error::SlunkCryptError;
use crate::ffi::libslunkcrypt::{self, SLUNKCRYPT_SUCCESS};
/// Generate a fresh random nonce, using a Cryptographically secure pseudorandom number generator.
pub fn generate_nonce() -> Result<u64, SlunkCryptError> {
let mut nonce: u64 = u64::default();
let retval = unsafe {
libslunkcrypt::slunkcrypt_generate_nonce(&mut nonce)
};
match retval {
SLUNKCRYPT_SUCCESS => Ok(nonce),
_ => Err(SlunkCryptError::from_retval(retval, "failed to generate nonce!"))
}
}
/// Returns the version of the native SlunkCrypt library.
pub fn get_version() -> (u16,u16,u16) {
unsafe {(
libslunkcrypt::SLUNKCRYPT_VERSION_MAJOR,
libslunkcrypt::SLUNKCRYPT_VERSION_MINOR,
libslunkcrypt::SLUNKCRYPT_VERSION_PATCH
)}
}
// Returns the version of the native SlunkCrypt library.
pub fn get_build() -> &'static str {
unsafe {
CStr::from_ptr(libslunkcrypt::SLUNKCRYPT_BUILD).to_str().unwrap()
}
}

43
binding/rust/src/lib.rs Normal file
View File

@ -0,0 +1,43 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
//! # SlunkCrypt Rust Wrapper
//! This crate exposes the functionality of the [**SlunkCrypt**](https://gitlab.com/lord_mulder/slunkcrypt) library to to Rust developers.
//!
//! Please see the [`SlunkCrypt`] struct for details!
//!
//! ## Build Instructions
//!
//! First the "native" SlunkCrypt library needs to be built, if not done yet:
//! ```sh
//! $ cd /home/JohnnyBeSlunk/dev/SlunkCrypt
//! $ make -B SHARED=1
//! ```
//!
//! Now build the SlunkCrypt Rust wrapper crate:
//! ```sh
//! $ cd binding/rust
//! $ cargo build --release
//! ```
//!
//! ## Unit Tests
//!
//! In order to run the unit tests, please type:
//!
//! ```sh
//! $ export LD_LIBRARY_PATH=/home/JohnnyBeSlunk/dev/SlunkCryptlibslunkcrypt/lib
//! $cargo test --release
//! ```
mod context;
mod error;
mod ffi;
mod functions;
mod passwd;
pub use context::SlunkCrypt;
pub use error::{SlunkCryptError, SlunkCryptErrorKind};
pub use functions::{generate_nonce, get_version, get_build};
pub use passwd::SlunkCryptPasswd;

View File

@ -0,0 +1,36 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
/// Passphrase to be used with [SlunkCrypt](crate::SlunkCrypt) encryption or decryption.
pub enum SlunkCryptPasswd<'a> {
/// A passphrase constructed from a `&str`
Str(&'a str),
/// A passphrase constructed from a `String`
String(String),
/// A passphrase constructed from `&[u8]`
Bytes(&'a[u8]),
/// A passphrase constructed from `Vec<u8>`
Vec(Vec<u8>)
}
impl<'a> SlunkCryptPasswd<'a> {
pub fn len(&self) -> usize {
match self {
Self::Str(str) => str.len(),
Self::String(str) => str.len(),
Self::Bytes(bin) => bin.len(),
Self::Vec(bin) => bin.len()
}
}
pub fn as_bytes(&'a self) -> &'a[u8] {
match self {
Self::Str(str) => str.as_bytes(),
Self::String(str) => str.as_bytes(),
Self::Bytes(bin) => bin,
Self::Vec(bin) => bin.as_slice()
}
}
}

View File

@ -0,0 +1,188 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use slunkcrypt_rs::{SlunkCryptPasswd, SlunkCrypt};
use hex;
use memx;
use rand::{rngs::OsRng, RngCore};
use std::{env, mem, cmp::Ordering};
const DEFAULT_TEST_LOOPS: usize = 8;
const PASSPHRASE: SlunkCryptPasswd = SlunkCryptPasswd::Str("eUGAwxOm0QwiVnDx");
const PASSPHRASE_BAD: SlunkCryptPasswd = SlunkCryptPasswd::Str("eUGAwxOm1QwiVnDx");
#[test]
fn test_create_instance() {
run_test(|| {
let (context, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
println!("EncryptContext: {:?}", context);
let context = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!");
println!("DecryptContext: {:?}", context);
});
}
#[test]
fn test_process() {
run_test(|| {
let (mut orig, mut encr, mut decr) = ([ 0u8; 5003 ], [ 0u8; 5003 ], [ 0u8; 5003 ]);
OsRng.fill_bytes(&mut orig);
println!("Plaintext: {}", hex::encode(&orig));
let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
context_enc.process(&orig, &mut encr).expect("Failed to encrypt data!");
mem::drop(context_enc);
println!("Encrypted: {}", hex::encode(&encr));
assert!(memx::memcmp(&orig, &encr) != Ordering::Equal);
let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!");
context_dec.process(&encr, &mut decr).expect("Failed to encrypt data!");
mem::drop(context_dec);
println!("Decrypted: {}", hex::encode(&decr));
});
}
#[test]
fn test_inplace() {
run_test(|| {
let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]);
OsRng.fill_bytes(&mut data);
memx::memcpy(&mut orig, &data).expect("Failed to copy original data!");
println!("Plaintext: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
context_enc.inplace(&mut data).expect("Failed to encrypt data!");
mem::drop(context_enc);
println!("Encrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!");
context_dec.inplace(&mut data).expect("Failed to decrypt data!");
mem::drop(context_dec);
println!("Decrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
});
}
#[test]
fn test_reset() {
run_test(|| {
let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]);
OsRng.fill_bytes(&mut data);
memx::memcpy(&mut orig, &data).expect("Failed to copy original data!");
println!("Plaintext: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
let (mut context, _nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
context.inplace(&mut data).expect("Failed to encrypt data!");
println!("Encrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
memx::memcpy(&mut data, &orig).expect("Failed to copy original data!");
println!("Plaintext: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
let nonce = context.reset_encrypt(&PASSPHRASE).expect("Failed to reset encryption context!");
context.inplace(&mut data).expect("Failed to encrypt data!");
println!("Encrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
context.reset_decrypt(&PASSPHRASE, nonce).expect("Failed to reset decryption context!");
context.inplace(&mut data).expect("Failed to decrypt data!");
mem::drop(context);
println!("Decrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
});
}
#[test]
fn test_fail_bad_nonce() {
run_test(|| {
let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]);
OsRng.fill_bytes(&mut data);
memx::memcpy(&mut orig, &data).expect("Failed to copy original data!");
println!("Plaintext: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
context_enc.inplace(&mut data).expect("Failed to encrypt data!");
mem::drop(context_enc);
println!("Encrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce + 1, None).expect("Failed to create decryption context!");
context_dec.inplace(&mut data).expect("Failed to decrypt data!");
mem::drop(context_dec);
println!("Decrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
});
}
#[test]
fn test_fail_bad_passwd() {
run_test(|| {
let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]);
OsRng.fill_bytes(&mut data);
memx::memcpy(&mut orig, &data).expect("Failed to copy original data!");
println!("Plaintext: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
context_enc.inplace(&mut data).expect("Failed to encrypt data!");
mem::drop(context_enc);
println!("Encrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE_BAD, nonce, None).expect("Failed to create decryption context!");
context_dec.inplace(&mut data).expect("Failed to decrypt data!");
mem::drop(context_dec);
println!("Decrypted: {}", hex::encode(&data));
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
});
}
#[test]
fn test_stress() {
let (mut data, mut orig) = ([ 0u8; u16::MAX as usize ], [ 0u8; u16::MAX as usize ]);
let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!");
let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!");
for i in 0..u16::MAX {
OsRng.fill_bytes(&mut data);
memx::memcpy(&mut orig, &data).expect("Failed to copy original data!");
context_enc.inplace(&mut data).expect("Failed to encrypt data!");
assert!(memx::memcmp(&data, &orig) != Ordering::Equal);
context_dec.inplace(&mut data).expect("Failed to decrypt data!");
assert!(memx::memcmp(&data, &orig) == Ordering::Equal);
if i % 499 == 0 {
println!("{:.1}%", (i as f64 / u16::MAX as f64) * 100.0)
}
}
}
fn run_test<F>(func: F) where F: Fn() {
let loops = env::var("TEST_LOOPS").map_or(DEFAULT_TEST_LOOPS, |value| value.parse::<usize>().unwrap());
for _ in 0..loops {
func();
}
}

View File

@ -0,0 +1,37 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
use std::env;
use slunkcrypt_rs::{get_version, get_build, generate_nonce};
const DEFAULT_TEST_LOOPS: usize = 8;
#[test]
fn test_get_version() {
let (major, minor, patch) = get_version();
assert!(major > 0);
println!("Version: {}.{}.{}", major, minor, patch);
}
#[test]
fn test_get_build() {
let build = get_build();
println!("Build: \"{}\"", build);
}
#[test]
fn test_init_generate_nonce() {
run_test(|| {
let nonce = generate_nonce().expect("Failed to generate nonce!");
println!("Nonce: {:16X}", nonce);
});
}
fn run_test<F>(func: F) where F: Fn() {
let loops = env::var("TEST_LOOPS").map_or(DEFAULT_TEST_LOOPS, |value| value.parse::<usize>().unwrap());
for _ in 0..loops {
func();
}
}

View File

@ -1,7 +1,17 @@
#!/bin/sh #!/bin/sh
if [ -n "$1" ]; then CC="$1"; elif [ -z "$CC" ]; then CC="cc"; fi if [ -n "$1" ]; then CC="$1"; elif [ -z "$CC" ]; then CC="cc"; fi
if [ -e /etc/os-release ]; then
PLATFORM_NAME="`cat /etc/os-release | egrep '^PRETTY_NAME=' | cut -c 13- | tr -d '\042'`"
elif [ -x /usr/bin/sw_vers ]; then
PLATFORM_NAME="`/usr/bin/sw_vers -productName` `/usr/bin/sw_vers -productVersion`"
fi
if [ -z "$PLATFORM_NAME" ]; then
PLATFORM_NAME="`uname -s -r -v -m`"
fi
echo "Built : `date -u +'%Y-%m-%d %H:%M:%S'`" echo "Built : `date -u +'%Y-%m-%d %H:%M:%S'`"
echo "Platform : `uname -s -r -v -m`" echo "Platform : $PLATFORM_NAME"
echo "Target : `$CC -dumpmachine`" echo "Target : `$CC -dumpmachine`"
echo "Compiler : `$CC -v 2>&1 | egrep -i '(gcc|clang)[[:space:]]+version' 2>/dev/null | head -n 1`" echo "Compiler : `$CC -v 2>&1 | egrep -i '(gcc|clang)[[:space:]]+version' 2>/dev/null | head -n 1`"

View File

@ -3,7 +3,7 @@ set -e
cd -- "$(dirname -- "${0}")/../../.." cd -- "$(dirname -- "${0}")/../../.."
if [ -z "${cc_path}" ]; then if [ -z "${cc_path}" ]; then
cc_path="/usr/local/bin/gcc13" cc_path="/usr/local/bin/gcc14"
fi fi
mk_slunk() { mk_slunk() {
@ -18,8 +18,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
gmake CC="${cc_path}" clean
mk_slunk 32 "i686" "pentiumpro" "pentium3" mk_slunk 32 "i686" "pentiumpro" "pentium3"
mk_slunk 64 "x86_64" "x86-64" "nocona" mk_slunk 64 "x86_64" "x86-64" "nocona"

View File

@ -15,9 +15,11 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
$BASH -x -c "make clean" if [ "$(arch)" == "x86_64" ]; then
mk_slunk "x86_64" "x86_64-gnu" "MARCH=x86-64 MTUNE=nocona"
mk_slunk "i686" "i686-gnu" "MARCH=pentiumpro MTUNE=pentium3" else
mk_slunk "i686" "i686-gnu" "MARCH=pentiumpro MTUNE=pentium3"
fi
./etc/build/build_info.sh "gcc" > "out/.build_info" ./etc/build/build_info.sh "gcc" > "out/.build_info"

View File

@ -14,8 +14,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
make clean
case "${BE_HOST_CPU}" in case "${BE_HOST_CPU}" in
x86_64) x86_64)
mk_slunk "gcc" "x86_64" "x86-64" "nocona" mk_slunk "gcc" "x86_64" "x86-64" "nocona"

View File

@ -19,8 +19,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
$BASH -x -c "make clean"
mk_slunk "x86_64" "x86_64-linux-gnu" "MARCH=x86-64 MTUNE=nocona" mk_slunk "x86_64" "x86_64-linux-gnu" "MARCH=x86-64 MTUNE=nocona"
mk_slunk "i686" "i686-linux-gnu" "MARCH=pentiumpro MTUNE=pentium3" mk_slunk "i686" "i686-linux-gnu" "MARCH=pentiumpro MTUNE=pentium3"

View File

@ -20,8 +20,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
$BASH -x -c "make clean"
mk_slunk "x86_64" "MARCH=x86-64 MTUNE=nocona" mk_slunk "x86_64" "MARCH=x86-64 MTUNE=nocona"
mk_slunk "i686" "MARCH=pentiumpro MTUNE=pentium3" mk_slunk "i686" "MARCH=pentiumpro MTUNE=pentium3"

View File

@ -13,8 +13,6 @@ mk_slunk() {
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
make CC="${cc_path}" clean
mk_slunk "x86_64" mk_slunk "x86_64"
mk_slunk "arm64" mk_slunk "arm64"

View File

@ -3,7 +3,7 @@ set -e
cd -- "$(dirname -- "${0}")/../../.." cd -- "$(dirname -- "${0}")/../../.."
if [ -z "${cc_path}" ]; then if [ -z "${cc_path}" ]; then
cc_path="/usr/pkg/gcc13/bin/gcc" cc_path="/usr/pkg/gcc14/bin/gcc"
fi fi
mk_slunk() { mk_slunk() {
@ -18,8 +18,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
gmake CC="${cc_path}" clean
case "$(uname -m)" in case "$(uname -m)" in
amd64) amd64)
mk_slunk "x86_64" "x86-64" "nocona" mk_slunk "x86_64" "x86-64" "nocona"

View File

@ -18,8 +18,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
gmake CC="${cc_path}" clean
case "$(uname -m)" in case "$(uname -m)" in
amd64) amd64)
mk_slunk "x86_64" "x86-64" "nocona" mk_slunk "x86_64" "x86-64" "nocona"

View File

@ -3,7 +3,7 @@ set -e
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../.." cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../.."
if [ -z "${cc_path}" ]; then if [ -z "${cc_path}" ]; then
cc_path="/usr/gcc/13/bin/gcc" cc_path="/usr/gcc/14/bin/gcc"
fi fi
mk_slunk() { mk_slunk() {
@ -18,8 +18,6 @@ fi
rm -rf "out" && mkdir -p "out" rm -rf "out" && mkdir -p "out"
gmake CC="${cc_path}" clean
mk_slunk 32 "i686" "pentiumpro" "pentium3" mk_slunk 32 "i686" "pentiumpro" "pentium3"
mk_slunk 64 "x86_64" "x86-64" "nocona" mk_slunk 64 "x86_64" "x86-64" "nocona"

View File

@ -23,12 +23,10 @@ fi
mk_slunk() { mk_slunk() {
make -B CC="${cc_path}" MARCH=${2} MTUNE=${3} STATIC=1 STRIP=1 FLTO=${use_flto} make -B CC="${cc_path}" MARCH=${2} MTUNE=${3} STATIC=1 STRIP=1 FLTO=${use_flto}
cp -vf "frontend/bin/slunkcrypt" "out/_next_/slunkcrypt-${1}" cp -vf "frontend/bin/slunkcrypt" "out/slunkcrypt-${1}"
} }
mkdir -p "out/_next_" mkdir -p "out"
make -B CC="${cc_path}" clean
case "${machine}" in case "${machine}" in
x86_64*) x86_64*)
@ -43,9 +41,9 @@ case "${machine}" in
esac esac
if [[ "${machine}" == *"-cygwin" ]]; then if [[ "${machine}" == *"-cygwin" ]]; then
cp -vfu "$(which cygwin1.dll)" "out/_next_" cp -vfu "$(which cygwin1.dll)" "out"
fi fi
./etc/build/build_info.sh "${cc_path}" > "out/_next_/.build_info" ./etc/build/build_info.sh "${cc_path}" > "out/.build_info"
echo "Build completed successfully." echo "Build completed successfully."

View File

@ -34,7 +34,6 @@ Section
!insertmacro PrintStatusMessage "Detecting operating system, please wait..." !insertmacro PrintStatusMessage "Detecting operating system, please wait..."
${IfNot} ${AtLeastBuild} 7601 ${IfNot} ${AtLeastBuild} 7601
MessageBox MB_ICONSTOP|MB_TOPMOST "This application runs on Windows 7 (SP1) or later!" MessageBox MB_ICONSTOP|MB_TOPMOST "This application runs on Windows 7 (SP1) or later!"
ExecShell "open" "https://support.microsoft.com/en-us/windows/install-windows-7-service-pack-1-sp1-b3da2c0f-cdb6-0572-8596-bab972897f61"
Quit Quit
${EndIf} ${EndIf}

View File

@ -19,6 +19,9 @@
#include <ctype.h> #include <ctype.h>
#include <assert.h> #include <assert.h>
/* Performance baseline (measured on Core i5 460M) */
#define PERF_FACTOR 104319.9
// ========================================================================== // ==========================================================================
// Self-test routines // Self-test routines
// ========================================================================== // ==========================================================================
@ -216,6 +219,7 @@ int selftest_routine(const size_t thread_count, const int debug)
const size_t total = ARRAY_SIZE(TEST_NONCE) * (ITERATIONS + (ITERATIONS * ARRAY_SIZE(TEST_STAGE))); const size_t total = ARRAY_SIZE(TEST_NONCE) * (ITERATIONS + (ITERATIONS * ARRAY_SIZE(TEST_STAGE)));
size_t count = 0U; size_t count = 0U;
uint64_t total_time = 0U;
FPRINTF(stderr, T("Self-test is in progress, please be patient... stage %2u/%2u "), 0U, (unsigned)total); FPRINTF(stderr, T("Self-test is in progress, please be patient... stage %2u/%2u "), 0U, (unsigned)total);
fflush(stderr); fflush(stderr);
@ -229,10 +233,12 @@ int selftest_routine(const size_t thread_count, const int debug)
{ {
FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u "), (unsigned)++count, (unsigned)total); FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u "), (unsigned)++count, (unsigned)total);
fflush(stderr); fflush(stderr);
const uint64_t clk_start = clock_read();
if (run_testcase(TEST_STAGE[i].text, TEST_NONCE[k], TEST_STAGE[i].check_orig, TEST_STAGE[i].check_encr[j][k], &param) != EXIT_SUCCESS) if (run_testcase(TEST_STAGE[i].text, TEST_NONCE[k], TEST_STAGE[i].check_orig, TEST_STAGE[i].check_encr[j][k], &param) != EXIT_SUCCESS)
{ {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
total_time += clock_read() - clk_start;
} }
} }
} }
@ -244,14 +250,16 @@ int selftest_routine(const size_t thread_count, const int debug)
{ {
FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u "), (unsigned)++count, (unsigned)total); FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u "), (unsigned)++count, (unsigned)total);
fflush(stderr); fflush(stderr);
const uint64_t clk_start = clock_read();
if (run_stresstest(TEST_NONCE[j], &param) != EXIT_SUCCESS) if (run_stresstest(TEST_NONCE[j], &param) != EXIT_SUCCESS)
{ {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
total_time += clock_read() - clk_start;
} }
} }
FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u\n\nCompleted successfully.\n\n"), (unsigned)total, (unsigned)total); FPRINTF(stderr, T("\b\b\b\b\b\b%2u/%2u\n\nCompleted successfully. (Score: %.0f)\n\n"), (unsigned)total, (unsigned)total, (double)clock_freq() / (double)total_time * PERF_FACTOR);
fflush(stderr); fflush(stderr);
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -7,6 +7,6 @@
<add key="DisableBusyIndicator" value="false"/> <add key="DisableBusyIndicator" value="false"/>
<add key="ThreadCount" value="0"/> <add key="ThreadCount" value="0"/>
<add key="KeepIncompleteFiles" value="false"/> <add key="KeepIncompleteFiles" value="false"/>
<add key="LegacyCompat" value="false"/> <add key="LegacyCompat" value="0"/>
</appSettings> </appSettings>
</configuration> </configuration>

View File

@ -6,7 +6,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using com.muldersoft.slunkcrypt.gui.utils; using com.muldersoft.slunkcrypt.gui.utils;
namespace com.muldersoft.slunkcrypt.gui.process namespace com.muldersoft.slunkcrypt.gui.process
@ -87,7 +86,7 @@ namespace com.muldersoft.slunkcrypt.gui.process
string.Equals(fileVersion.CompanyName, "Muldersoft", StringComparison.OrdinalIgnoreCase) && string.Equals(fileVersion.CompanyName, "Muldersoft", StringComparison.OrdinalIgnoreCase) &&
(fileVersion.FileMajorPart == appVersion.Major) && (fileVersion.FileMinorPart == appVersion.Minor)) (fileVersion.FileMajorPart == appVersion.Major) && (fileVersion.FileMinorPart == appVersion.Minor))
{ {
success = (fileVersion.FilePrivatePart >= appVersion.Revision); success = ToVersion64(fileVersion.FileBuildPart, fileVersion.FilePrivatePart) >= ToVersion64(appVersion.Build, appVersion.Revision);
} }
} }
finally finally
@ -101,5 +100,10 @@ namespace com.muldersoft.slunkcrypt.gui.process
catch { } catch { }
return success; return success;
} }
static ulong ToVersion64(int upper, int lower)
{
return (Convert.ToUInt64(upper) << 32) | Convert.ToUInt64(lower);
}
} }
} }

View File

@ -19,6 +19,8 @@ namespace com.muldersoft.slunkcrypt.gui.process
{ {
public enum Mode { Encrypt, Decrypt } public enum Mode { Encrypt, Decrypt }
public enum Error { Checksum, Password }
public struct SlunkOptions public struct SlunkOptions
{ {
public SlunkOptions(bool keepIncompleteFiles, int threadCount, bool enableLegacyCompat) public SlunkOptions(bool keepIncompleteFiles, int threadCount, bool enableLegacyCompat)
@ -40,7 +42,14 @@ namespace com.muldersoft.slunkcrypt.gui.process
private const bool ENABLE_DEBUG_LOGGING = false; private const bool ENABLE_DEBUG_LOGGING = false;
#endif #endif
private static readonly Regex RX_PROGRESS = new Regex(@"(\d+)\.(\d)%", RegexOptions.Compiled); private static readonly Regex RX_PROGRESS = new Regex(@"(\d+)\.(\d)%", RegexOptions.Compiled | RegexOptions.CultureInvariant);
private static readonly IReadOnlyDictionary<Error, Regex> RX_ERROR = new Dictionary<Error, Regex>
{
{ Error.Checksum, new Regex(@"\bChecksum\s+mismatch\s+detected\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) },
{ Error.Password, new Regex(@"\bThe\s+given\s+passphrase\s+is\s+forbidden\s+as\s+a\s+precautionary\s+measure\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) }
}
.ToReadOnlyDictionary();
private readonly FileStream m_executableFile; private readonly FileStream m_executableFile;
@ -63,12 +72,15 @@ namespace com.muldersoft.slunkcrypt.gui.process
{ {
throw new ArgumentException("Invalid SlunkCrypt parameters!"); throw new ArgumentException("Invalid SlunkCrypt parameters!");
} }
Dictionary<string, string> environmentVariables = new Dictionary<string, string>(); Dictionary<string, string> environmentVariables = new Dictionary<string, string>
environmentVariables.Add("SLUNK_PASSPHRASE", password); {
environmentVariables.Add("SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false))); { "SLUNK_PASSPHRASE", password },
environmentVariables.Add("SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0)))); { "SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false)) },
environmentVariables.Add("SLUNK_LEGACY_COMPAT", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.enableLegacyCompat : false))); { "SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0))) },
environmentVariables.Add("SLUNK_DEBUG_LOGGING", Convert.ToString(Convert.ToInt32(ENABLE_DEBUG_LOGGING))); { "SLUNK_LEGACY_COMPAT", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.enableLegacyCompat : false)) },
{ "SLUNK_DEBUG_LOGGING", Convert.ToString(Convert.ToInt32(ENABLE_DEBUG_LOGGING)) }
};
ErrorState = null;
return await ExecAsnyc(m_executableFile.Name, new string[] { GetCommandString(mode), inputFile, outputFile }, Path.GetDirectoryName(outputFile), environmentVariables); return await ExecAsnyc(m_executableFile.Name, new string[] { GetCommandString(mode), inputFile, outputFile }, Path.GetDirectoryName(outputFile), environmentVariables);
} }
@ -82,23 +94,34 @@ namespace com.muldersoft.slunkcrypt.gui.process
catch { } catch { }
} }
public Error? ErrorState { get; private set; } = null;
// ============================================================================= // =============================================================================
// Internal methods // Internal methods
// ============================================================================= // =============================================================================
protected override double ParseProgressString(ref string currentLine, bool stream) protected override double ParseProgressString(ref string currentLine, bool stream)
{ {
double temp, result = double.NaN;
int index = int.MaxValue; int index = int.MaxValue;
Match match = RX_PROGRESS.Match(currentLine); double temp = double.NaN, result = double.NaN;
while (match.Success) if (!ErrorState.HasValue)
{
foreach (KeyValuePair<Error, Regex> errorRegex in RX_ERROR)
{
if (errorRegex.Value.IsMatch(currentLine))
{
ErrorState = errorRegex.Key;
break;
}
}
}
for (Match match = RX_PROGRESS.Match(currentLine); match.Success; match = match.NextMatch())
{ {
if (TryParseProgressValue(match, out temp)) if (TryParseProgressValue(match, out temp))
{ {
result = temp; result = double.IsNaN(result) ? temp : Math.Max(result, temp);
} }
index = Math.Min(index, match.Index); index = Math.Min(index, match.Index);
match = RX_PROGRESS.Match(currentLine, match.Index + match.Length);
} }
if (index != int.MaxValue) if (index != int.MaxValue)
{ {

View File

@ -9,6 +9,6 @@ namespace com.muldersoft.slunkcrypt.gui.Properties
{ {
public const string VERS_MAJOR = "1"; public const string VERS_MAJOR = "1";
public const string VERS_MINOR = "3"; public const string VERS_MINOR = "3";
public const string VERS_PATCH = "1"; public const string VERS_PATCH = "2";
} }
} }

View File

@ -82,6 +82,7 @@
<DependentUpon>PasswordToggleBox.xaml</DependentUpon> <DependentUpon>PasswordToggleBox.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Properties\_Version.cs" /> <Compile Include="Properties\_Version.cs" />
<Compile Include="Utilities\DictionaryHelper.cs" />
<Compile Include="Utilities\EnumHelper.cs" /> <Compile Include="Utilities\EnumHelper.cs" />
<Compile Include="Process\ExecutableHelper.cs" /> <Compile Include="Process\ExecutableHelper.cs" />
<Compile Include="Utilities\ApplicationConfig.cs" /> <Compile Include="Utilities\ApplicationConfig.cs" />

View File

@ -49,7 +49,7 @@ namespace com.muldersoft.slunkcrypt.gui
private volatile int m_isInitialized = 0; private volatile int m_isInitialized = 0;
private volatile ModeOfOperation m_modeOfOperation = (ModeOfOperation)(-1); private volatile ModeOfOperation m_modeOfOperation = (ModeOfOperation)(-1);
private volatile bool m_busyFlag = false, m_checksumError = false, m_processReceived = false, m_disableAnimation = false; private volatile bool m_busyFlag = false, m_processReceived = false, m_disableAnimation = false;
private volatile SlunkCryptRunner m_processRunner = null; private volatile SlunkCryptRunner m_processRunner = null;
private uint? m_menuId_disableAnimation = null, m_menuId_enableExpertMode = null; private uint? m_menuId_disableAnimation = null, m_menuId_enableExpertMode = null;
@ -68,6 +68,10 @@ namespace com.muldersoft.slunkcrypt.gui
m_dispatcherTimer.Interval = TimeSpan.FromMilliseconds(331); m_dispatcherTimer.Interval = TimeSpan.FromMilliseconds(331);
m_logFileReadOnly = new ReadOnlyObservableCollection<string>(m_logFile); m_logFileReadOnly = new ReadOnlyObservableCollection<string>(m_logFile);
m_disableAnimation = m_config.DisableBusyIndicator; m_disableAnimation = m_config.DisableBusyIndicator;
if (m_config.LegacyCompat < 1)
{
Checkbox_Encrypt_LegacyCompat.Visibility = Checkbox_Decrypt_LegacyCompat.Visibility = Visibility.Collapsed;
}
} }
// ============================================================================= // =============================================================================
@ -125,7 +129,7 @@ namespace com.muldersoft.slunkcrypt.gui
{ {
Hint_SoftwareRendering.Visibility = Visibility.Visible; Hint_SoftwareRendering.Visibility = Visibility.Visible;
} }
if (m_config.LegacyCompat) if (m_config.LegacyCompat > 1)
{ {
Checkbox_Encrypt_LegacyCompat.IsChecked = Checkbox_Decrypt_LegacyCompat.IsChecked = true; Checkbox_Encrypt_LegacyCompat.IsChecked = Checkbox_Decrypt_LegacyCompat.IsChecked = true;
} }
@ -273,10 +277,6 @@ namespace com.muldersoft.slunkcrypt.gui
private void Process_OutputAvailable(string line, bool stderr) private void Process_OutputAvailable(string line, bool stderr)
{ {
AppendLogFile(line); AppendLogFile(line);
if (line.IndexOf("Checksum mismatch detected!", StringComparison.OrdinalIgnoreCase) >= 0)
{
m_checksumError = true;
}
} }
private void Porcess_ProgressChanged(double progress) private void Porcess_ProgressChanged(double progress)
@ -522,7 +522,7 @@ namespace com.muldersoft.slunkcrypt.gui
private async Task ValidatePassword(string inputFilePath, string outputFilePath, PasswordToggleBox passwordEdit, CheckBox legacyCheckBox, SlunkProcessor processor, bool checkStrongPasswd) private async Task ValidatePassword(string inputFilePath, string outputFilePath, PasswordToggleBox passwordEdit, CheckBox legacyCheckBox, SlunkProcessor processor, bool checkStrongPasswd)
{ {
bool enableLegacyCompat = legacyCheckBox.IsChecked.GetValueOrDefault(); bool enableLegacyCompat = (m_config.LegacyCompat > 0) && legacyCheckBox.IsChecked.GetValueOrDefault(m_config.LegacyCompat > 1);
string passwordStr; string passwordStr;
if (string.IsNullOrEmpty(passwordStr = passwordEdit.Password) || (passwordStr.Length < MIN_PASSWD_LENGTH)) if (string.IsNullOrEmpty(passwordStr = passwordEdit.Password) || (passwordStr.Length < MIN_PASSWD_LENGTH))
{ {
@ -548,7 +548,7 @@ namespace com.muldersoft.slunkcrypt.gui
return; return;
} }
} }
if (enableLegacyCompat && (!m_config.LegacyCompat)) if (enableLegacyCompat && (m_config.LegacyCompat < 2))
{ {
if (MessageBox.Show(this, "Legacy compat-mode should not be used to encrypt new files!", "Legacy Compatibility", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) != MessageBoxResult.OK) if (MessageBox.Show(this, "Legacy compat-mode should not be used to encrypt new files!", "Legacy Compatibility", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) != MessageBoxResult.OK)
{ {
@ -567,7 +567,7 @@ namespace com.muldersoft.slunkcrypt.gui
ResetKeyboardFocus(this); ResetKeyboardFocus(this);
SetProgress(double.PositiveInfinity); SetProgress(double.PositiveInfinity);
ClearLogFile(); ClearLogFile();
Button_Decrypt_Toggle.IsChecked = Button_Encrypt_Toggle.IsChecked = m_checksumError = m_processReceived = false; Button_Decrypt_Toggle.IsChecked = Button_Encrypt_Toggle.IsChecked = m_processReceived = false;
if (!await processor(inputFile, outputFile, password, enableLegacyCompat)) if (!await processor(inputFile, outputFile, password, enableLegacyCompat))
{ {
if (!m_config.KeepIncompleteFiles) if (!m_config.KeepIncompleteFiles)
@ -583,10 +583,10 @@ namespace com.muldersoft.slunkcrypt.gui
private async Task<bool> Encrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat) private async Task<bool> Encrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat)
{ {
SetStatus("Please wait while the encryption process is initializing..."); SetStatus("Please wait while the encryption process is initializing...");
int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password, enableLegacyCompat); Tuple<int, SlunkCryptRunner.Error?> result = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password, enableLegacyCompat);
if (exitCode.HasValue) if (!ReferenceEquals(result, null))
{ {
if (exitCode.Value == 0) if (result.Item1 == 0)
{ {
SetProgress(1); SetProgress(1);
SetStatus("Completed: The file has been encrypted successfully.", Status.Success); SetStatus("Completed: The file has been encrypted successfully.", Status.Success);
@ -594,8 +594,16 @@ namespace com.muldersoft.slunkcrypt.gui
} }
else else
{ {
switch (result.Item2)
{
case SlunkCryptRunner.Error.Password:
SetStatus("Error: The specified passphrase is forbidden! (contained in OWASP database)", Status.Failure);
break;
default:
SetStatus("Error: Failed to enecrypt the file. Please see the log file for details!", Status.Failure);
break;
}
SetProgress(1, true); SetProgress(1, true);
SetStatus("Error: Failed to enecrypt the file. Please see the log file for details!", Status.Failure);
SystemSounds.Hand.Play(); SystemSounds.Hand.Play();
} }
return true; return true;
@ -606,10 +614,10 @@ namespace com.muldersoft.slunkcrypt.gui
private async Task<bool> Decrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat) private async Task<bool> Decrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat)
{ {
SetStatus("Please wait while the decryption process is initializing..."); SetStatus("Please wait while the decryption process is initializing...");
int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password, enableLegacyCompat); Tuple<int, SlunkCryptRunner.Error?> result = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password, enableLegacyCompat);
if (exitCode.HasValue) if (!ReferenceEquals(result, null))
{ {
if (exitCode.Value == 0) if (result.Item1 == 0)
{ {
SetStatus("Completed: The file has been decrypted successfully (checksum is correct).", Status.Success); SetStatus("Completed: The file has been decrypted successfully (checksum is correct).", Status.Success);
SetProgress(1); SetProgress(1);
@ -617,13 +625,14 @@ namespace com.muldersoft.slunkcrypt.gui
} }
else else
{ {
if (m_checksumError) switch (result.Item2)
{ {
SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure); case SlunkCryptRunner.Error.Checksum:
} SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure);
else break;
{ default:
SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure); SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure);
break;
} }
SetProgress(1, true); SetProgress(1, true);
SystemSounds.Hand.Play(); SystemSounds.Hand.Play();
@ -633,7 +642,7 @@ namespace com.muldersoft.slunkcrypt.gui
return false; return false;
} }
private async Task<int?> RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password, bool enableLegacyCompat) private async Task<Tuple<int, SlunkCryptRunner.Error?>> RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password, bool enableLegacyCompat)
{ {
if (!ReferenceEquals(m_processRunner, null)) if (!ReferenceEquals(m_processRunner, null))
{ {
@ -647,7 +656,8 @@ namespace com.muldersoft.slunkcrypt.gui
m_processRunner.OutputAvailable += Process_OutputAvailable; m_processRunner.OutputAvailable += Process_OutputAvailable;
m_processRunner.ProgressChanged += Porcess_ProgressChanged; m_processRunner.ProgressChanged += Porcess_ProgressChanged;
SetProcessPriority(ProcessPriorityClass.AboveNormal); SetProcessPriority(ProcessPriorityClass.AboveNormal);
return await m_processRunner.ExecuteAsync(mode, inputFile, outputFile, password, options); int exitCode = await m_processRunner.ExecuteAsync(mode, inputFile, outputFile, password, options);
return Tuple.Create(exitCode, m_processRunner.ErrorState);
} }
} }
catch (ProcessRunner.ProcessStartException err) catch (ProcessRunner.ProcessStartException err)

View File

@ -34,15 +34,15 @@ namespace com.muldersoft.slunkcrypt.gui.utils
{ {
get get
{ {
return ComputeIfAbsent("ThreadCount", (key) => AppConfHelper.GetConfigValueAsInt32(key).GetValueOrDefault(0)); return ComputeIfAbsent("ThreadCount", (key) => Clamp(0, AppConfHelper.GetConfigValueAsInt32(key).GetValueOrDefault(0), 32));
} }
} }
public bool LegacyCompat public int LegacyCompat
{ {
get get
{ {
return ComputeIfAbsent("LegacyCompat", (key) => AppConfHelper.GetConfigValueAsBool(key).GetValueOrDefault(false)); return ComputeIfAbsent("LegacyCompat", (key) => Clamp(0, AppConfHelper.GetConfigValueAsInt32(key).GetValueOrDefault(0), 2));
} }
} }
@ -60,6 +60,11 @@ namespace com.muldersoft.slunkcrypt.gui.utils
return Convert.ToBoolean(m_cache.GetOrAdd(name, (key) => Convert.ToInt32(valueFactory(key)))); return Convert.ToBoolean(m_cache.GetOrAdd(name, (key) => Convert.ToInt32(valueFactory(key))));
} }
protected static int Clamp(int min, int value, int max)
{
return (value < min) ? min : ((value > max) ? max : value);
}
// ============================================================================= // =============================================================================
// Helper class // Helper class
// ============================================================================= // =============================================================================
@ -116,6 +121,14 @@ namespace com.muldersoft.slunkcrypt.gui.utils
{ {
return result; return result;
} }
else
{
bool boolean;
if (bool.TryParse(value.Trim(), out boolean))
{
return Convert.ToInt32(boolean);
}
}
} }
return null; return null;
} }

View File

@ -0,0 +1,18 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace com.muldersoft.slunkcrypt.gui.utils
{
public static class DictionaryHelper
{
public static IReadOnlyDictionary<K, V> ToReadOnlyDictionary<K, V>(this IDictionary<K, V> dict)
{
return (dict is ReadOnlyDictionary<K, V>) ? ((ReadOnlyDictionary<K, V>)dict) : new ReadOnlyDictionary<K, V>(dict);
}
}
}

View File

@ -23,10 +23,11 @@ namespace com.muldersoft.slunkcrypt.gui.utils
public static new string ToString() public static new string ToString()
{ {
Version version = m_version.Value; Version value = Version;
return string.Format( string versionString = string.Format(
(version.Revision > 0) ? "Version {0:D}.{1:D}.{2:D}, built on {3}" : "Version {0:D}.{1:D}, built on {3}", ((value.Revision > 0) || (value.Build > 0)) ? ((value.Build > 0) ? "{0:D}.{1:D}.{2:D}.{3:D}" : "{0:D}.{1:D}.{3:D}") : "{0:D}.{1:D}",
version.Major, version.Minor, version.Revision, BuildDate.ToString("yyyy-MM-dd")); value.Major, value.Minor, value.Build, value.Revision);
return string.Format("Version {0}, built on {1}", versionString, BuildDate.ToString("yyyy-MM-dd"));
} }
// ============================================================================= // =============================================================================

View File

@ -8,6 +8,6 @@
#define LIB_VERSION_MAJOR 1 #define LIB_VERSION_MAJOR 1
#define LIB_VERSION_MINOR 3 #define LIB_VERSION_MINOR 3
#define LIB_VERSION_PATCH 1 #define LIB_VERSION_PATCH 2
#endif #endif