aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorben2019-05-27 21:40:41 +0200
committerben2019-05-27 21:40:41 +0200
commitaccdbc780617a4f4b30790ffb25ab1d26652c315 (patch)
tree2633c1246a35e7242bfc693959830ae63b641301
downloadinexact-accdbc780617a4f4b30790ffb25ab1d26652c315.tar.gz
inexact-accdbc780617a4f4b30790ffb25ab1d26652c315.tar.bz2
inexact-accdbc780617a4f4b30790ffb25ab1d26652c315.tar.xz
First public release
-rw-r--r--Makefile64
-rw-r--r--README.md138
-rw-r--r--src/argon2-arch.c20
-rw-r--r--src/argon2-core.c615
-rw-r--r--src/argon2-core.h226
-rw-r--r--src/argon2-encoding.c432
-rw-r--r--src/argon2-encoding.h40
-rw-r--r--src/argon2-impl-select.c93
-rw-r--r--src/argon2-impl-select.h23
-rw-r--r--src/argon2-template-64.h193
-rw-r--r--src/argon2-thread.c36
-rw-r--r--src/argon2-thread.h47
-rw-r--r--src/argon2.c476
-rw-r--r--src/argon2.h465
-rw-r--r--src/argtable3.c5018
-rw-r--r--src/argtable3.h305
-rw-r--r--src/base64.c253
-rw-r--r--src/base64.h21
-rw-r--r--src/binhex.c67
-rw-r--r--src/binhex.h9
-rw-r--r--src/blake2-impl.h90
-rw-r--r--src/blake2.c225
-rw-r--r--src/blake2.h30
-rw-r--r--src/chacha20_drng.c675
-rw-r--r--src/chacha20_drng.h154
-rw-r--r--src/curve25519-donna-32bit.h466
-rw-r--r--src/curve25519-donna-64bit.h345
-rw-r--r--src/curve25519-donna-common.h43
-rw-r--r--src/curve25519-donna-portable-identify.h103
-rw-r--r--src/curve25519-donna-portable.h92
-rw-r--r--src/curve25519-donna-scalarmult-base.h66
-rw-r--r--src/curve25519-donna.h32
-rw-r--r--src/curve25519.c27
-rw-r--r--src/curve25519.h10
-rw-r--r--src/inexact.c1033
-rw-r--r--src/inexact.h40
-rw-r--r--src/main.c555
-rw-r--r--src/norx_inexact.c701
-rw-r--r--src/norx_inexact.h64
-rw-r--r--src/norx_inexact_config.h20
-rw-r--r--src/norx_inexact_util.h119
-rw-r--r--src/randombytes.c262
-rw-r--r--src/randombytes.h19
-rw-r--r--src/readpassphrase.c283
-rw-r--r--src/readpassphrase.h23
-rw-r--r--src/sha3.c296
-rw-r--r--src/sha3.h50
-rw-r--r--src/tests.c813
-rw-r--r--src/tests.h19
-rwxr-xr-xtest.sh71
50 files changed, 15267 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..70fe875
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+CC=gcc
+CFLAGS=-Wall -Werror -pthread -O3
+LDFLAGS=
+
+default: inexact
+
+windows: CC=x86_64-w64-mingw32-gcc
+windows: CFLAGS=-Wall -Werror -pthread -O3
+windows: LDFLAGS=
+windows: inexact
+
+windows-debug: CC=x86_64-w64-mingw32-gcc
+windows-debug: CFLAGS=-Wall -Werror -pthread -O3 -g
+windows-debug: LDFLAGS=
+windows-debug: inexact
+
+debug: CFLAGS=-Wall -Werror -pthread -O3 -g
+debug: LDFLAGS=
+debug: inexact
+
+argtable3.o: src/argtable3.c
+ mkdir -p objs
+ $(CC) $(CFLAGS) -c src/argtable3.c -o objs/argtable3.o
+
+base64.o: src/base64.c
+ $(CC) $(CFLAGS) -c src/base64.c -o objs/base64.o
+
+binhex.o: src/binhex.c
+ $(CC) $(CFLAGS) -c src/binhex.c -o objs/binhex.o
+
+curve25519.o: src/curve25519.c
+ $(CC) $(CFLAGS) -c src/curve25519.c -o objs/curve25519.o
+
+inexact.o: src/inexact.c
+ $(CC) $(CFLAGS) -c src/inexact.c -o objs/inexact.o
+
+norx_inexact.o: src/norx_inexact.c
+ $(CC) $(CFLAGS) -c src/norx_inexact.c -o objs/norx_inexact.o
+
+sha3.o: src/sha3.c
+ $(CC) $(CFLAGS) -c src/sha3.c -o objs/sha3.o
+
+tests.o: src/tests.c
+ $(CC) $(CFLAGS) -c src/tests.c -o objs/tests.o
+
+readpassphrase.o: src/readpassphrase.c
+ $(CC) $(CFLAGS) -c src/readpassphrase.c -o objs/readpassphrase.o
+
+randombytes.o: src/randombytes.c
+ $(CC) $(CFLAGS) -c src/randombytes.c -o objs/randombytes.o
+
+chacha20_drng.o: src/chacha20_drng.c
+ $(CC) $(CFLAGS) -c src/chacha20_drng.c -o objs/chacha20_drng.o
+
+blake2.o: src/blake2.c
+ $(CC) $(CFLAGS) -c src/blake2.c -o objs/blake2.o
+
+inexact: src/main.c argtable3.o base64.o binhex.o curve25519.o inexact.o norx_inexact.o sha3.o tests.o readpassphrase.o chacha20_drng.o randombytes.o blake2.o
+ $(CC) $(CFLAGS) src/argon2.c src/argon2-core.c src/argon2-thread.c src/argon2-encoding.c src/argon2-impl-select.c src/argon2-arch.c src/main.c objs/argtable3.o objs/base64.o objs/binhex.o objs/curve25519.o objs/inexact.o objs/norx_inexact.o objs/sha3.o objs/tests.o objs/readpassphrase.o objs/randombytes.o objs/chacha20_drng.o objs/blake2.o -o inexact $(LDFLAGS)
+
+clean:
+ -rm objs/*.o
+ -rm -f inexact inexact.exe
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..74ca362
--- /dev/null
+++ b/README.md
@@ -0,0 +1,138 @@
+# INadvisable EXperimental Asymmetric Crypto Tool #
+
+Inexact is an experimental cryptographic tools, multi-platform, scriptable
+complying with the KISS principle (Keep it simple and stupid).
+
+The main features are as follow:
+
+- Asymmetric encryption.
+- Symmetric encryption.
+- Variable length of encrypted messages for the same input message.
+- Authenticated encrypted messages.
+- Shell redirection compliant (using pipe).
+- Base64 or modified base64 encoding output compatible with a URL path, DNS entry, or file name.
+
+
+** WARNING : Inexact uses recent algorithms not approved nor by NIST or NSA ! **
+
+## Encryption principles
+
+Inexact implements the following algorithms:
+
+- Norx 256bits
+- Diffie-Hellman X25219
+- SHA3-256
+- DRNG chacha20
+- Argon2
+
+An encrypted message is split into two parts:
+
+- The first one containing the parameters of the second part.
+- The encrypted message.
+
+Rest of protocol:
+
+- The asymmetric encryption is achieved by using Diffie-Hellman with a 25519
+ elliptical curve shared secret.
+- A random buffer (rand1) with random size is generated from the chacha20
+ algorithm.
+- The shared secret from the Diffie-Hellman is hashed with rand1 buffer using
+ SHA3-256 and then used as a key for the Norx algorithm.
+- The nonce for the Norx function of the second part is a SHA3-256 hash of
+ parameters of the first part and rand1 buffer.
+- Argon2 is used as a challenge for the password of the private key.
+- The symmetric encryption is based on ian asymmetric encryption by adding argon2
+ challenge nonce and public key in the encrypted message.
+
+Schematic:
+
+```
+|----------------------------------------------------------------------------------------------------------------------------------
+| <encrypted 0 with len(tag) = 4> | tag0[4] | <rand> | <encrypted 1 with len(tag) = Y> | tag1[Y] |
+| header[8]: len(part 0 + part 1) | | len(rand) = X | header: params | |
+| key: sha3-256(nonce0+shared_secret) | | X >= 8 | key1: sha3-256(nonce1+shared_secret) | |
+| nonce0: sha3-256 (rand+encrypted1) | | | nonce1: sha3-256(params+rand) | |
+| message[5]=params:len(nonce1)=X len(tag1)=Y | | | message: data | |
+|------------------ part 0 [9] --------------------------- |------------------------ part 1 -------------------------------------- |
+
+```
+
+
+## How to build
+
+For GNU/Linux and Mac OS X:
+
+```
+ cd inexact
+ make
+```
+
+For Microsoft Windows using cross compilation on GNU/Linux:
+
+```
+ cd inexact
+ make windows
+```
+
+## Usage
+
+Asymmetric encryption:
+
+```
+ ./inexact --no-password -g -k alices.key -p alicep.key
+ ./inexact --no-password -g -k bobs.key -p bobp.key
+ echo "coucou alice" | ./inexact -e -k bobs.key -p alicep.key
+ rl9adQvAj20I3TIVDJrT3iSSrauqXKlF13nW91QfV80MopMNTCFLURfBHyLPurFnoFByRxv7kUvMkswzn8FoN4ibAkFizkmcpiMMnxWUQpIB3EhFsAg
+ echo "rl9adQvAj20I3TIVDJrT3iSSrauqXKlF13nW91QfV80MopMNTCFLURfBHyLPurFnoFByRxv7kUvMkswzn8FoN4ibAkFizkmcpiMMnxWUQpIB3EhFsAg" | ./inexact -d -k alices.key -p
+bobp.key
+ coucou alice
+```
+
+Symmetric encryption:
+
+```
+ echo "my secret" | ./inexact -e -s
+ Password :
+ Verifying, please re-enter :
+ C3B6_AMg7qPbYHDFE35hJzDFYIK40k48FiqCu1gQqSsNsU_7j8qZhh9vlzKvm_507fns1bih1tLZesKQfjLXFiauyskNBT6SmJazunQiesadWnoi1v2kye68lgfc96dOjk7F6pc2okGnNzzpv0SnRPNSGEq44
+fZ53IS6AIT89pmVAj631vBr95S2mQ7_Rj_99CaQ
+ echo
+"C3B6_AMg7qPbYHDFE35hJzDFYIK40k48FiqCu1gQqSsNsU_7j8qZhh9vlzKvm_507fns1bih1tLZesKQfjLXFiauyskNBT6SmJazunQiesadWnoi1v2kye68lgfc96dOjk7F6pc2okGnNzzpv0SnRPNSGEq44f
+Z53IS6AIT89pmVAj631vBr95S2mQ7_Rj_99CaQ" | ./inexact -d -s
+ Password :
+ my secret
+```
+
+Base64 output:
+
+```
+ cat Makefile | ./inexact -e -k bobs.key -p alicep.key --base64 | base64 -d | xz -z > crypted_compressed
+ cat crypted_compressed | xz -d | base64 | ./inexact -d -k alices.key -p bobp.key
+```
+
+Variable encrypted message size (smallest):
+
+```
+ echo "coucou Bob" | ./inexact -e -k alices.key -p bobp.key -w
+```
+
+Variable encrypted message size (400 chars):
+
+```
+ echo "coucou Bob" | ./inexact -e -k alices.key -p bobp.key -c 400
+```
+
+## Credits
+
+Inexact uses this libraries:
+
+- https://github.com/smuellerDD/chacha20_drng
+- https://github.com/floodyberry/curve25519-donna
+- http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c
+- https://github.com/brainhub/SHA3IUF
+- https://github.com/norx
+- https://github.com/WOnder93/argon2
+- https://nachtimwald.com/2017/09/24/hex-encode-and-decode-in-c/
+- https://github.com/argtable/argtable3
+- https://github.com/dsprenkels/randombytes
+
diff --git a/src/argon2-arch.c b/src/argon2-arch.c
new file mode 100644
index 0000000..ec0f049
--- /dev/null
+++ b/src/argon2-arch.c
@@ -0,0 +1,20 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "argon2-impl-select.h"
+
+#define rotr64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
+
+#include "argon2-template-64.h"
+
+void fill_segment_default(const argon2_instance_t *instance,
+ argon2_position_t position)
+{
+ fill_segment_64(instance, position);
+}
+
+void argon2_get_impl_list(argon2_impl_list *list)
+{
+ list->count = 0;
+}
diff --git a/src/argon2-core.c b/src/argon2-core.c
new file mode 100644
index 0000000..82bf968
--- /dev/null
+++ b/src/argon2-core.c
@@ -0,0 +1,615 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with
+ * this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+/*For memory wiping*/
+#ifdef _MSC_VER
+#include <windows.h>
+#include <winbase.h> /* For SecureZeroMemory */
+#endif
+#if defined __STDC_LIB_EXT1__
+#define __STDC_WANT_LIB_EXT1__ 1
+#endif
+#define VC_GE_2005(version) (version >= 1400)
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "argon2-core.h"
+#include "argon2-thread.h"
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#if defined(__clang__)
+#if __has_attribute(optnone)
+#define NOT_OPTIMIZED __attribute__((optnone))
+#endif
+#elif defined(__GNUC__)
+#define GCC_VERSION \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION >= 40400
+#define NOT_OPTIMIZED __attribute__((optimize("O0")))
+#endif
+#endif
+#ifndef NOT_OPTIMIZED
+#define NOT_OPTIMIZED
+#endif
+
+/***************Instance and Position constructors**********/
+void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); }
+
+void copy_block(block *dst, const block *src) {
+ memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
+}
+
+void xor_block(block *dst, const block *src) {
+ int i;
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
+ dst->v[i] ^= src->v[i];
+ }
+}
+
+static void load_block(block *dst, const void *input) {
+ unsigned i;
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
+ dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
+ }
+}
+
+static void store_block(void *output, const block *src) {
+ unsigned i;
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
+ store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
+ }
+}
+
+/***************Memory functions*****************/
+
+int allocate_memory(const argon2_context *context,
+ argon2_instance_t *instance) {
+ size_t blocks = instance->memory_blocks;
+ size_t memory_size = blocks * ARGON2_BLOCK_SIZE;
+
+ /* 0. Check for memory supplied by user: */
+ /* NOTE: Sufficient memory size is already checked in argon2_ctx_mem() */
+ if (instance->memory != NULL) {
+ return ARGON2_OK;
+ }
+
+ /* 1. Check for multiplication overflow */
+ if (blocks != 0 && memory_size / ARGON2_BLOCK_SIZE != blocks) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ /* 2. Try to allocate with appropriate allocator */
+ if (context->allocate_cbk) {
+ (context->allocate_cbk)((uint8_t **)&instance->memory, memory_size);
+ } else {
+ instance->memory = malloc(memory_size);
+ }
+
+ if (instance->memory == NULL) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ return ARGON2_OK;
+}
+
+void free_memory(const argon2_context *context,
+ const argon2_instance_t *instance) {
+ size_t memory_size = instance->memory_blocks * ARGON2_BLOCK_SIZE;
+
+ clear_internal_memory(instance->memory, memory_size);
+
+ if (instance->keep_memory) {
+ /* user-supplied memory -- do not free */
+ return;
+ }
+
+ if (context->free_cbk) {
+ (context->free_cbk)((uint8_t *)instance->memory, memory_size);
+ } else {
+ free(instance->memory);
+ }
+}
+
+void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) {
+#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER)
+ SecureZeroMemory(v, n);
+#elif defined memset_s
+ memset_s(v, n, 0, n);
+#elif defined(__OpenBSD__)
+ explicit_bzero(v, n);
+#else
+ static void *(*const volatile memset_sec)(void *, int, size_t) = &memset;
+ memset_sec(v, 0, n);
+#endif
+}
+
+/* Memory clear flag defaults to true. */
+int FLAG_clear_internal_memory = 1;
+void clear_internal_memory(void *v, size_t n) {
+ if (FLAG_clear_internal_memory && v) {
+ secure_wipe_memory(v, n);
+ }
+}
+
+void finalize(const argon2_context *context, argon2_instance_t *instance) {
+ if (context != NULL && instance != NULL) {
+ block blockhash;
+ uint32_t l;
+
+ copy_block(&blockhash, instance->memory + instance->lane_length - 1);
+
+ /* XOR the last blocks */
+ for (l = 1; l < instance->lanes; ++l) {
+ uint32_t last_block_in_lane =
+ l * instance->lane_length + (instance->lane_length - 1);
+ xor_block(&blockhash, instance->memory + last_block_in_lane);
+ }
+
+ /* Hash the result */
+ {
+ uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+ store_block(blockhash_bytes, &blockhash);
+ blake2b_long(context->out, context->outlen, blockhash_bytes,
+ ARGON2_BLOCK_SIZE);
+ /* clear blockhash and blockhash_bytes */
+ clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE);
+ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
+ }
+
+ free_memory(context, instance);
+ }
+}
+
+uint32_t index_alpha(const argon2_instance_t *instance,
+ const argon2_position_t *position, uint32_t pseudo_rand,
+ int same_lane) {
+ /*
+ * Pass 0:
+ * This lane : all already finished segments plus already constructed
+ * blocks in this segment
+ * Other lanes : all already finished segments
+ * Pass 1+:
+ * This lane : (SYNC_POINTS - 1) last segments plus already constructed
+ * blocks in this segment
+ * Other lanes : (SYNC_POINTS - 1) last segments
+ */
+ uint32_t reference_area_size;
+ uint64_t relative_position;
+ uint32_t start_position, absolute_position;
+
+ if (0 == position->pass) {
+ /* First pass */
+ if (0 == position->slice) {
+ /* First slice */
+ reference_area_size =
+ position->index - 1; /* all but the previous */
+ } else {
+ if (same_lane) {
+ /* The same lane => add current segment */
+ reference_area_size =
+ position->slice * instance->segment_length +
+ position->index - 1;
+ } else {
+ reference_area_size =
+ position->slice * instance->segment_length +
+ ((position->index == 0) ? (-1) : 0);
+ }
+ }
+ } else {
+ /* Second pass */
+ if (same_lane) {
+ reference_area_size = instance->lane_length -
+ instance->segment_length + position->index -
+ 1;
+ } else {
+ reference_area_size = instance->lane_length -
+ instance->segment_length +
+ ((position->index == 0) ? (-1) : 0);
+ }
+ }
+
+ /* 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
+ * relative position */
+ relative_position = pseudo_rand;
+ relative_position = relative_position * relative_position >> 32;
+ relative_position = reference_area_size - 1 -
+ (reference_area_size * relative_position >> 32);
+
+ /* 1.2.5 Computing starting position */
+ start_position = 0;
+
+ if (0 != position->pass) {
+ start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
+ ? 0
+ : (position->slice + 1) * instance->segment_length;
+ }
+
+ /* 1.2.6. Computing absolute position */
+ absolute_position = (start_position + relative_position) %
+ instance->lane_length; /* absolute position */
+ return absolute_position;
+}
+
+#ifdef _WIN32
+static unsigned __stdcall fill_segment_thr(void *thread_data)
+#else
+static void *fill_segment_thr(void *thread_data)
+#endif
+{
+ argon2_thread_data *my_data = thread_data;
+ fill_segment(my_data->instance_ptr, my_data->pos);
+ argon2_thread_exit();
+ return 0;
+}
+
+/* Single-threaded version for p=1 case */
+static int fill_memory_blocks_st(argon2_instance_t *instance) {
+ uint32_t r, s, l;
+
+ for (r = 0; r < instance->passes; ++r) {
+ for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
+ for (l = 0; l < instance->lanes; ++l) {
+ argon2_position_t position = { r, l, (uint8_t)s, 0 };
+ fill_segment(instance, position);
+ }
+ }
+ }
+ return ARGON2_OK;
+}
+
+/* Multi-threaded version for p > 1 case */
+static int fill_memory_blocks_mt(argon2_instance_t *instance) {
+ uint32_t r, s;
+ argon2_thread_handle_t *thread = NULL;
+ argon2_thread_data *thr_data = NULL;
+ int rc = ARGON2_OK;
+
+ /* 1. Allocating space for threads */
+ thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t));
+ if (thread == NULL) {
+ rc = ARGON2_MEMORY_ALLOCATION_ERROR;
+ goto fail;
+ }
+
+ thr_data = calloc(instance->lanes, sizeof(argon2_thread_data));
+ if (thr_data == NULL) {
+ rc = ARGON2_MEMORY_ALLOCATION_ERROR;
+ goto fail;
+ }
+
+ for (r = 0; r < instance->passes; ++r) {
+ for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
+ uint32_t l;
+
+ /* 2. Calling threads */
+ for (l = 0; l < instance->lanes; ++l) {
+ argon2_position_t position;
+
+ /* 2.1 Join a thread if limit is exceeded */
+ if (l >= instance->threads) {
+ if (argon2_thread_join(thread[l - instance->threads])) {
+ rc = ARGON2_THREAD_FAIL;
+ goto fail;
+ }
+ }
+
+ /* 2.2 Create thread */
+ position.pass = r;
+ position.lane = l;
+ position.slice = (uint8_t)s;
+ position.index = 0;
+ thr_data[l].instance_ptr =
+ instance; /* preparing the thread input */
+ memcpy(&(thr_data[l].pos), &position,
+ sizeof(argon2_position_t));
+ if (argon2_thread_create(&thread[l], &fill_segment_thr,
+ (void *)&thr_data[l])) {
+ rc = ARGON2_THREAD_FAIL;
+ goto fail;
+ }
+
+ /* fill_segment(instance, position); */
+ /*Non-thread equivalent of the lines above */
+ }
+
+ /* 3. Joining remaining threads */
+ for (l = instance->lanes - instance->threads; l < instance->lanes;
+ ++l) {
+ if (argon2_thread_join(thread[l])) {
+ rc = ARGON2_THREAD_FAIL;
+ goto fail;
+ }
+ }
+ }
+ }
+
+fail:
+ if (thread != NULL) {
+ free(thread);
+ }
+ if (thr_data != NULL) {
+ free(thr_data);
+ }
+ return rc;
+}
+
+int fill_memory_blocks(argon2_instance_t *instance) {
+ if (instance == NULL || instance->lanes == 0) {
+ return ARGON2_INCORRECT_PARAMETER;
+ }
+
+ return instance->threads == 1 ?
+ fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance);
+}
+
+int validate_inputs(const argon2_context *context) {
+ if (NULL == context) {
+ return ARGON2_INCORRECT_PARAMETER;
+ }
+
+ if (NULL == context->out) {
+ return ARGON2_OUTPUT_PTR_NULL;
+ }
+
+ /* Validate output length */
+ if (ARGON2_MIN_OUTLEN > context->outlen) {
+ return ARGON2_OUTPUT_TOO_SHORT;
+ }
+
+ if (ARGON2_MAX_OUTLEN < context->outlen) {
+ return ARGON2_OUTPUT_TOO_LONG;
+ }
+
+ /* Validate password (required param) */
+ if (NULL == context->pwd) {
+ if (0 != context->pwdlen) {
+ return ARGON2_PWD_PTR_MISMATCH;
+ }
+ }
+
+ if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) {
+ return ARGON2_PWD_TOO_SHORT;
+ }
+
+ if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) {
+ return ARGON2_PWD_TOO_LONG;
+ }
+
+ /* Validate salt (required param) */
+ if (NULL == context->salt) {
+ if (0 != context->saltlen) {
+ return ARGON2_SALT_PTR_MISMATCH;
+ }
+ }
+
+ if (ARGON2_MIN_SALT_LENGTH > context->saltlen) {
+ return ARGON2_SALT_TOO_SHORT;
+ }
+
+ if (ARGON2_MAX_SALT_LENGTH < context->saltlen) {
+ return ARGON2_SALT_TOO_LONG;
+ }
+
+ /* Validate secret (optional param) */
+ if (NULL == context->secret) {
+ if (0 != context->secretlen) {
+ return ARGON2_SECRET_PTR_MISMATCH;
+ }
+ } else {
+ if (ARGON2_MIN_SECRET > context->secretlen) {
+ return ARGON2_SECRET_TOO_SHORT;
+ }
+ if (ARGON2_MAX_SECRET < context->secretlen) {
+ return ARGON2_SECRET_TOO_LONG;
+ }
+ }
+
+ /* Validate associated data (optional param) */
+ if (NULL == context->ad) {
+ if (0 != context->adlen) {
+ return ARGON2_AD_PTR_MISMATCH;
+ }
+ } else {
+ if (ARGON2_MIN_AD_LENGTH > context->adlen) {
+ return ARGON2_AD_TOO_SHORT;
+ }
+ if (ARGON2_MAX_AD_LENGTH < context->adlen) {
+ return ARGON2_AD_TOO_LONG;
+ }
+ }
+
+ /* Validate memory cost */
+ if (ARGON2_MIN_MEMORY > context->m_cost) {
+ return ARGON2_MEMORY_TOO_LITTLE;
+ }
+
+ if (ARGON2_MAX_MEMORY < context->m_cost) {
+ return ARGON2_MEMORY_TOO_MUCH;
+ }
+
+ if (context->m_cost < 8 * context->lanes) {
+ return ARGON2_MEMORY_TOO_LITTLE;
+ }
+
+ /* Validate time cost */
+ if (ARGON2_MIN_TIME > context->t_cost) {
+ return ARGON2_TIME_TOO_SMALL;
+ }
+
+ if (ARGON2_MAX_TIME < context->t_cost) {
+ return ARGON2_TIME_TOO_LARGE;
+ }
+
+ /* Validate lanes */
+ if (ARGON2_MIN_LANES > context->lanes) {
+ return ARGON2_LANES_TOO_FEW;
+ }
+
+ if (ARGON2_MAX_LANES < context->lanes) {
+ return ARGON2_LANES_TOO_MANY;
+ }
+
+ /* Validate threads */
+ if (ARGON2_MIN_THREADS > context->threads) {
+ return ARGON2_THREADS_TOO_FEW;
+ }
+
+ if (ARGON2_MAX_THREADS < context->threads) {
+ return ARGON2_THREADS_TOO_MANY;
+ }
+
+ if (NULL != context->allocate_cbk && NULL == context->free_cbk) {
+ return ARGON2_FREE_MEMORY_CBK_NULL;
+ }
+
+ if (NULL == context->allocate_cbk && NULL != context->free_cbk) {
+ return ARGON2_ALLOCATE_MEMORY_CBK_NULL;
+ }
+
+ return ARGON2_OK;
+}
+
+void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
+ uint32_t l;
+ /* Make the first and second block in each lane as G(H0||0||i) or
+ G(H0||1||i) */
+ uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+ for (l = 0; l < instance->lanes; ++l) {
+
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
+ blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
+ ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&instance->memory[l * instance->lane_length + 0],
+ blockhash_bytes);
+
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
+ blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
+ ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&instance->memory[l * instance->lane_length + 1],
+ blockhash_bytes);
+ }
+ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
+}
+
+void initial_hash(uint8_t *blockhash, argon2_context *context,
+ argon2_type type) {
+ blake2b_state BlakeHash;
+ uint8_t value[sizeof(uint32_t)];
+
+ if (NULL == context || NULL == blockhash) {
+ return;
+ }
+
+ blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
+
+ store32(&value, context->lanes);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->outlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->m_cost);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->t_cost);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->version);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, (uint32_t)type);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->pwdlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->pwd != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
+ context->pwdlen);
+
+ if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) {
+ secure_wipe_memory(context->pwd, context->pwdlen);
+ context->pwdlen = 0;
+ }
+ }
+
+ store32(&value, context->saltlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->salt != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->salt,
+ context->saltlen);
+ }
+
+ store32(&value, context->secretlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->secret != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
+ context->secretlen);
+
+ if (context->flags & ARGON2_FLAG_CLEAR_SECRET) {
+ secure_wipe_memory(context->secret, context->secretlen);
+ context->secretlen = 0;
+ }
+ }
+
+ store32(&value, context->adlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->ad != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
+ context->adlen);
+ }
+
+ blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
+}
+
+int initialize(argon2_instance_t *instance, argon2_context *context) {
+ uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
+ int result = ARGON2_OK;
+
+ if (instance == NULL || context == NULL)
+ return ARGON2_INCORRECT_PARAMETER;
+ instance->context_ptr = context;
+
+ /* 1. Memory allocation */
+
+ result = allocate_memory(context, instance);
+ if (result != ARGON2_OK) {
+ return result;
+ }
+
+ /* 2. Initial hashing */
+ /* H_0 + 8 extra bytes to produce the first blocks */
+ /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */
+ /* Hashing all inputs */
+ initial_hash(blockhash, context, instance->type);
+ /* Zeroing 8 extra bytes */
+ clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
+ ARGON2_PREHASH_SEED_LENGTH -
+ ARGON2_PREHASH_DIGEST_LENGTH);
+
+ /* 3. Creating first blocks, we always have at least two blocks in a slice
+ */
+ fill_first_blocks(blockhash, instance);
+ /* Clearing the hash */
+ clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH);
+
+ return ARGON2_OK;
+}
diff --git a/src/argon2-core.h b/src/argon2-core.h
new file mode 100644
index 0000000..f55d2dd
--- /dev/null
+++ b/src/argon2-core.h
@@ -0,0 +1,226 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with
+ * this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef ARGON2_CORE_H
+#define ARGON2_CORE_H
+
+#include "argon2.h"
+
+#if defined(_MSC_VER)
+#define _ALIGN(n) __declspec(align(16))
+#elif defined(__GNUC__) || defined(__clang)
+#define _ALIGN(x) __attribute__((__aligned__(x)))
+#else
+#define _ALIGN(x)
+#endif
+
+#define CONST_CAST(x) (x)(uintptr_t)
+
+/**********************Argon2 internal constants*******************************/
+
+enum argon2_core_constants {
+ /* Memory block size in bytes */
+ ARGON2_BLOCK_SIZE = 1024,
+ ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
+ ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
+
+ /* Number of pseudo-random values generated by one call to Blake in Argon2i
+ to
+ generate reference block positions */
+ ARGON2_ADDRESSES_IN_BLOCK = 128,
+
+ /* Pre-hashing digest length and its extension*/
+ ARGON2_PREHASH_DIGEST_LENGTH = 64,
+ ARGON2_PREHASH_SEED_LENGTH = 72
+};
+
+/*************************Argon2 internal data types***********************/
+
+/*
+ * Structure for the (1KB) memory block implemented as 128 64-bit words.
+ * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
+ * bounds checking).
+ */
+typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
+
+/*****************Functions that work with the block******************/
+
+/* Initialize each byte of the block with @in */
+void init_block_value(block *b, uint8_t in);
+
+/* Copy block @src to block @dst */
+void copy_block(block *dst, const block *src);
+
+/* XOR @src onto @dst bytewise */
+void xor_block(block *dst, const block *src);
+
+/*
+ * Argon2 instance: memory pointer, number of passes, amount of memory, type,
+ * and derived values.
+ * Used to evaluate the number and location of blocks to construct in each
+ * thread
+ */
+typedef struct Argon2_instance_t {
+ block *memory; /* Memory pointer */
+ uint32_t version;
+ uint32_t passes; /* Number of passes */
+ uint32_t memory_blocks; /* Number of blocks in memory */
+ uint32_t segment_length;
+ uint32_t lane_length;
+ uint32_t lanes;
+ uint32_t threads;
+ argon2_type type;
+ int print_internals; /* whether to print the memory blocks */
+ int keep_memory;
+ argon2_context *context_ptr; /* points back to original context */
+} argon2_instance_t;
+
+/*
+ * Argon2 position: where we construct the block right now. Used to distribute
+ * work between threads.
+ */
+typedef struct Argon2_position_t {
+ uint32_t pass;
+ uint32_t lane;
+ uint8_t slice;
+ uint32_t index;
+} argon2_position_t;
+
+/*Struct that holds the inputs for thread handling FillSegment*/
+typedef struct Argon2_thread_data {
+ argon2_instance_t *instance_ptr;
+ argon2_position_t pos;
+} argon2_thread_data;
+
+/*************************Argon2 core functions********************************/
+
+/* Allocates memory to the given pointer, uses the appropriate allocator as
+ * specified in the context. Total allocated memory is num*size.
+ * @param context argon2_context which specifies the allocator
+ * @param instance the Argon2 instance
+ * @return ARGON2_OK if memory is allocated successfully
+ */
+int allocate_memory(const argon2_context *context,
+ argon2_instance_t *instance);
+
+/*
+ * Frees memory at the given pointer, uses the appropriate deallocator as
+ * specified in the context. Also cleans the memory using clear_internal_memory.
+ * @param context argon2_context which specifies the deallocator
+ * @param instance the Argon2 instance
+ */
+void free_memory(const argon2_context *context,
+ const argon2_instance_t *instance);
+
+/* Function that securely cleans the memory. This ignores any flags set
+ * regarding clearing memory. Usually one just calls clear_internal_memory.
+ * @param mem Pointer to the memory
+ * @param s Memory size in bytes
+ */
+void secure_wipe_memory(void *v, size_t n);
+
+/* Function that securely clears the memory if FLAG_clear_internal_memory is
+ * set. If the flag isn't set, this function does nothing.
+ * @param mem Pointer to the memory
+ * @param s Memory size in bytes
+ */
+ARGON2_PUBLIC void clear_internal_memory(void *v, size_t n);
+
+/*
+ * Computes absolute position of reference block in the lane following a skewed
+ * distribution and using a pseudo-random value as input
+ * @param instance Pointer to the current instance
+ * @param position Pointer to the current position
+ * @param pseudo_rand 32-bit pseudo-random value used to determine the position
+ * @param same_lane Indicates if the block will be taken from the current lane.
+ * If so we can reference the current segment
+ * @pre All pointers must be valid
+ */
+uint32_t index_alpha(const argon2_instance_t *instance,
+ const argon2_position_t *position, uint32_t pseudo_rand,
+ int same_lane);
+
+/*
+ * Function that validates all inputs against predefined restrictions and return
+ * an error code
+ * @param context Pointer to current Argon2 context
+ * @return ARGON2_OK if everything is all right, otherwise one of error codes
+ * (all defined in <argon2.h>
+ */
+int validate_inputs(const argon2_context *context);
+
+/*
+ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
+ * password and secret if needed
+ * @param context Pointer to the Argon2 internal structure containing memory
+ * pointer, and parameters for time and space requirements.
+ * @param blockhash Buffer for pre-hashing digest
+ * @param type Argon2 type
+ * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
+ * allocated
+ */
+void initial_hash(uint8_t *blockhash, argon2_context *context,
+ argon2_type type);
+
+/*
+ * Function creates first 2 blocks per lane
+ * @param instance Pointer to the current instance
+ * @param blockhash Pointer to the pre-hashing digest
+ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
+ */
+void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance);
+
+/*
+ * Function allocates memory, hashes the inputs with Blake, and creates first
+ * two blocks. Returns the pointer to the main memory with 2 blocks per lane
+ * initialized
+ * @param context Pointer to the Argon2 internal structure containing memory
+ * pointer, and parameters for time and space requirements.
+ * @param instance Current Argon2 instance
+ * @return Zero if successful, -1 if memory failed to allocate. @context->state
+ * will be modified if successful.
+ */
+int initialize(argon2_instance_t *instance, argon2_context *context);
+
+/*
+ * XORing the last block of each lane, hashing it, making the tag. Deallocates
+ * the memory.
+ * @param context Pointer to current Argon2 context (use only the out parameters
+ * from it)
+ * @param instance Pointer to current instance of Argon2
+ * @pre instance->state must point to necessary amount of memory
+ * @pre context->out must point to outlen bytes of memory
+ * @pre if context->free_cbk is not NULL, it should point to a function that
+ * deallocates memory
+ */
+void finalize(const argon2_context *context, argon2_instance_t *instance);
+
+/*
+ * Function that fills the segment using previous segments also from other
+ * threads
+ * @param instance Pointer to the current instance
+ * @param position Current position
+ * @pre all block pointers must be valid
+ */
+void fill_segment(const argon2_instance_t *instance,
+ argon2_position_t position);
+
+/*
+ * Function that fills the entire memory t_cost times based on the first two
+ * blocks in each lane
+ * @param instance Pointer to the current instance
+ * @return ARGON2_OK if successful, @context->state
+ */
+int fill_memory_blocks(argon2_instance_t *instance);
+
+#endif
diff --git a/src/argon2-encoding.c b/src/argon2-encoding.c
new file mode 100644
index 0000000..12da473
--- /dev/null
+++ b/src/argon2-encoding.c
@@ -0,0 +1,432 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "argon2-encoding.h"
+#include "argon2-core.h"
+
+/*
+ * Example code for a decoder and encoder of "hash strings", with Argon2
+ * parameters.
+ *
+ * This code comprises three sections:
+ *
+ * -- The first section contains generic Base64 encoding and decoding
+ * functions. It is conceptually applicable to any hash function
+ * implementation that uses Base64 to encode and decode parameters,
+ * salts and outputs. It could be made into a library, provided that
+ * the relevant functions are made public (non-static) and be given
+ * reasonable names to avoid collisions with other functions.
+ *
+ * -- The second section is specific to Argon2. It encodes and decodes
+ * the parameters, salts and outputs. It does not compute the hash
+ * itself.
+ *
+ * The code was originally written by Thomas Pornin <pornin@bolet.org>,
+ * to whom comments and remarks may be sent. It is released under what
+ * should amount to Public Domain or its closest equivalent; the
+ * following mantra is supposed to incarnate that fact with all the
+ * proper legal rituals:
+ *
+ * ---------------------------------------------------------------------
+ * This file is provided under the terms of Creative Commons CC0 1.0
+ * Public Domain Dedication. To the extent possible under law, the
+ * author (Thomas Pornin) has waived all copyright and related or
+ * neighboring rights to this file. This work is published from: Canada.
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (c) 2015 Thomas Pornin
+ */
+
+/* ==================================================================== */
+/*
+ * Common code; could be shared between different hash functions.
+ *
+ * Note: the Base64 functions below assume that uppercase letters (resp.
+ * lowercase letters) have consecutive numerical codes, that fit on 8
+ * bits. All modern systems use ASCII-compatible charsets, where these
+ * properties are true. If you are stuck with a dinosaur of a system
+ * that still defaults to EBCDIC then you already have much bigger
+ * interoperability issues to deal with.
+ */
+
+/*
+ * Some macros for constant-time comparisons. These work over values in
+ * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
+ */
+#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
+#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
+#define GE(x, y) (GT(y, x) ^ 0xFF)
+#define LT(x, y) GT(y, x)
+#define LE(x, y) GE(y, x)
+
+/*
+ * Convert value x (0..63) to corresponding Base64 character.
+ */
+static int b64_byte_to_char(unsigned x) {
+ return (LT(x, 26) & (x + 'A')) |
+ (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
+ (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') |
+ (EQ(x, 63) & '/');
+}
+
+/*
+ * Convert character c to the corresponding 6-bit value. If character c
+ * is not a Base64 character, then 0xFF (255) is returned.
+ */
+static unsigned b64_char_to_byte(int c) {
+ unsigned x;
+
+ x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
+ (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
+ (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
+ (EQ(c, '/') & 63);
+ return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
+}
+
+/*
+ * Convert some bytes to Base64. 'dst_len' is the length (in characters)
+ * of the output buffer 'dst'; if that buffer is not large enough to
+ * receive the result (including the terminating 0), then (size_t)-1
+ * is returned. Otherwise, the zero-terminated Base64 string is written
+ * in the buffer, and the output length (counted WITHOUT the terminating
+ * zero) is returned.
+ */
+static size_t to_base64(char *dst, size_t dst_len, const void *src,
+ size_t src_len) {
+ size_t olen;
+ const unsigned char *buf;
+ unsigned acc, acc_len;
+
+ olen = (src_len / 3) << 2;
+ switch (src_len % 3) {
+ case 2:
+ olen++;
+ /* fall through */
+ case 1:
+ olen += 2;
+ break;
+ }
+ if (dst_len <= olen) {
+ return (size_t)-1;
+ }
+ acc = 0;
+ acc_len = 0;
+ buf = (const unsigned char *)src;
+ while (src_len-- > 0) {
+ acc = (acc << 8) + (*buf++);
+ acc_len += 8;
+ while (acc_len >= 6) {
+ acc_len -= 6;
+ *dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F);
+ }
+ }
+ if (acc_len > 0) {
+ *dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
+ }
+ *dst++ = 0;
+ return olen;
+}
+
+/*
+ * Decode Base64 chars into bytes. The '*dst_len' value must initially
+ * contain the length of the output buffer '*dst'; when the decoding
+ * ends, the actual number of decoded bytes is written back in
+ * '*dst_len'.
+ *
+ * Decoding stops when a non-Base64 character is encountered, or when
+ * the output buffer capacity is exceeded. If an error occurred (output
+ * buffer is too small, invalid last characters leading to unprocessed
+ * buffered bits), then NULL is returned; otherwise, the returned value
+ * points to the first non-Base64 character in the source stream, which
+ * may be the terminating zero.
+ */
+static const char *from_base64(void *dst, size_t *dst_len, const char *src) {
+ size_t len;
+ unsigned char *buf;
+ unsigned acc, acc_len;
+
+ buf = (unsigned char *)dst;
+ len = 0;
+ acc = 0;
+ acc_len = 0;
+ for (;;) {
+ unsigned d;
+
+ d = b64_char_to_byte(*src);
+ if (d == 0xFF) {
+ break;
+ }
+ src++;
+ acc = (acc << 6) + d;
+ acc_len += 6;
+ if (acc_len >= 8) {
+ acc_len -= 8;
+ if ((len++) >= *dst_len) {
+ return NULL;
+ }
+ *buf++ = (acc >> acc_len) & 0xFF;
+ }
+ }
+
+ /*
+ * If the input length is equal to 1 modulo 4 (which is
+ * invalid), then there will remain 6 unprocessed bits;
+ * otherwise, only 0, 2 or 4 bits are buffered. The buffered
+ * bits must also all be zero.
+ */
+ if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
+ return NULL;
+ }
+ *dst_len = len;
+ return src;
+}
+
+/*
+ * Decode decimal integer from 'str'; the value is written in '*v'.
+ * Returned value is a pointer to the next non-decimal character in the
+ * string. If there is no digit at all, or the value encoding is not
+ * minimal (extra leading zeros), or the value does not fit in an
+ * 'unsigned long', then NULL is returned.
+ */
+static const char *decode_decimal(const char *str, unsigned long *v) {
+ const char *orig;
+ unsigned long acc;
+
+ acc = 0;
+ for (orig = str;; str++) {
+ int c;
+
+ c = *str;
+ if (c < '0' || c > '9') {
+ break;
+ }
+ c -= '0';
+ if (acc > (ULONG_MAX / 10)) {
+ return NULL;
+ }
+ acc *= 10;
+ if ((unsigned long)c > (ULONG_MAX - acc)) {
+ return NULL;
+ }
+ acc += (unsigned long)c;
+ }
+ if (str == orig || (*orig == '0' && str != (orig + 1))) {
+ return NULL;
+ }
+ *v = acc;
+ return str;
+}
+
+/* ==================================================================== */
+/*
+ * Code specific to Argon2.
+ *
+ * The code below applies the following format:
+ *
+ * $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>
+ *
+ * where <T> is either 'd', 'id', or 'i', <num> is a decimal integer (positive,
+ * fits in an 'unsigned long'), and <bin> is Base64-encoded data (no '=' padding
+ * characters, no newline or whitespace).
+ *
+ * The last two binary chunks (encoded in Base64) are, in that order,
+ * the salt and the output. Both are required. The binary salt length and the
+ * output length must be in the allowed ranges defined in argon2.h.
+ *
+ * The ctx struct must contain buffers large enough to hold the salt and pwd
+ * when it is fed into decode_string.
+ */
+
+int decode_string(argon2_context *ctx, const char *str, argon2_type type) {
+
+/* check for prefix */
+#define CC(prefix) \
+ do { \
+ size_t cc_len = strlen(prefix); \
+ if (strncmp(str, prefix, cc_len) != 0) { \
+ return ARGON2_DECODING_FAIL; \
+ } \
+ str += cc_len; \
+ } while ((void)0, 0)
+
+/* optional prefix checking with supplied code */
+#define CC_opt(prefix, code) \
+ do { \
+ size_t cc_len = strlen(prefix); \
+ if (strncmp(str, prefix, cc_len) == 0) { \
+ str += cc_len; \
+ { code; } \
+ } \
+ } while ((void)0, 0)
+
+/* Decoding prefix into uint32_t decimal */
+#define DECIMAL_U32(x) \
+ do { \
+ unsigned long dec_x; \
+ str = decode_decimal(str, &dec_x); \
+ if (str == NULL || dec_x > UINT32_MAX) { \
+ return ARGON2_DECODING_FAIL; \
+ } \
+ (x) = (uint32_t)dec_x; \
+ } while ((void)0, 0)
+
+/* Decoding base64 into a binary buffer */
+#define BIN(buf, max_len, len) \
+ do { \
+ size_t bin_len = (max_len); \
+ str = from_base64(buf, &bin_len, str); \
+ if (str == NULL || bin_len > UINT32_MAX) { \
+ return ARGON2_DECODING_FAIL; \
+ } \
+ (len) = (uint32_t)bin_len; \
+ } while ((void)0, 0)
+
+ size_t maxsaltlen = ctx->saltlen;
+ size_t maxoutlen = ctx->outlen;
+ int validation_result;
+ const char* type_string;
+
+ /* We should start with the argon2_type we are using */
+ type_string = argon2_type2string(type, 0);
+ if (!type_string) {
+ return ARGON2_INCORRECT_TYPE;
+ }
+
+ CC("$");
+ CC(type_string);
+
+ /* Reading the version number if the default is suppressed */
+ ctx->version = ARGON2_VERSION_10;
+ CC_opt("$v=", DECIMAL_U32(ctx->version));
+
+ CC("$m=");
+ DECIMAL_U32(ctx->m_cost);
+ CC(",t=");
+ DECIMAL_U32(ctx->t_cost);
+ CC(",p=");
+ DECIMAL_U32(ctx->lanes);
+ ctx->threads = ctx->lanes;
+
+ CC("$");
+ BIN(ctx->salt, maxsaltlen, ctx->saltlen);
+ CC("$");
+ BIN(ctx->out, maxoutlen, ctx->outlen);
+
+ /* The rest of the fields get the default values */
+ ctx->secret = NULL;
+ ctx->secretlen = 0;
+ ctx->ad = NULL;
+ ctx->adlen = 0;
+ ctx->allocate_cbk = NULL;
+ ctx->free_cbk = NULL;
+ ctx->flags = ARGON2_DEFAULT_FLAGS;
+
+ /* On return, must have valid context */
+ validation_result = validate_inputs(ctx);
+ if (validation_result != ARGON2_OK) {
+ return validation_result;
+ }
+
+ /* Can't have any additional characters */
+ if (*str == 0) {
+ return ARGON2_OK;
+ } else {
+ return ARGON2_DECODING_FAIL;
+ }
+#undef CC
+#undef CC_opt
+#undef DECIMAL_U32
+#undef BIN
+}
+
+int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
+ argon2_type type) {
+#define SS(str) \
+ do { \
+ size_t pp_len = strlen(str); \
+ if (pp_len >= dst_len) { \
+ return ARGON2_ENCODING_FAIL; \
+ } \
+ memcpy(dst, str, pp_len + 1); \
+ dst += pp_len; \
+ dst_len -= pp_len; \
+ } while ((void)0, 0)
+
+#define SX(x) \
+ do { \
+ char tmp[30]; \
+ sprintf(tmp, "%lu", (unsigned long)(x)); \
+ SS(tmp); \
+ } while ((void)0, 0)
+
+#define SB(buf, len) \
+ do { \
+ size_t sb_len = to_base64(dst, dst_len, buf, len); \
+ if (sb_len == (size_t)-1) { \
+ return ARGON2_ENCODING_FAIL; \
+ } \
+ dst += sb_len; \
+ dst_len -= sb_len; \
+ } while ((void)0, 0)
+
+ const char* type_string = argon2_type2string(type, 0);
+ int validation_result = validate_inputs(ctx);
+
+ if (!type_string) {
+ return ARGON2_ENCODING_FAIL;
+ }
+
+ if (validation_result != ARGON2_OK) {
+ return validation_result;
+ }
+
+ SS("$");
+ SS(type_string);
+
+ SS("$v=");
+ SX(ctx->version);
+
+ SS("$m=");
+ SX(ctx->m_cost);
+ SS(",t=");
+ SX(ctx->t_cost);
+ SS(",p=");
+ SX(ctx->lanes);
+
+ SS("$");
+ SB(ctx->salt, ctx->saltlen);
+
+ SS("$");
+ SB(ctx->out, ctx->outlen);
+ return ARGON2_OK;
+
+#undef SS
+#undef SX
+#undef SB
+}
+
+size_t b64len(uint32_t len) {
+ size_t olen = ((size_t)len / 3) << 2;
+
+ switch (len % 3) {
+ case 2:
+ olen++;
+ /* fall through */
+ case 1:
+ olen += 2;
+ break;
+ }
+
+ return olen;
+}
+
+size_t numlen(uint32_t num) {
+ size_t len = 1;
+ while (num >= 10) {
+ ++len;
+ num = num / 10;
+ }
+ return len;
+}
+
diff --git a/src/argon2-encoding.h b/src/argon2-encoding.h
new file mode 100644
index 0000000..e7834e4
--- /dev/null
+++ b/src/argon2-encoding.h
@@ -0,0 +1,40 @@
+#ifndef ENCODING_H
+#define ENCODING_H
+#include "argon2.h"
+
+#define ARGON2_MAX_DECODED_LANES UINT32_C(255)
+#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8)
+#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12)
+
+/*
+* encode an Argon2 hash string into the provided buffer. 'dst_len'
+* contains the size, in characters, of the 'dst' buffer; if 'dst_len'
+* is less than the number of required characters (including the
+* terminating 0), then this function returns ARGON2_ENCODING_ERROR.
+*
+* on success, ARGON2_OK is returned.
+*/
+int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
+ argon2_type type);
+
+/*
+* Decodes an Argon2 hash string into the provided structure 'ctx'.
+* The only fields that must be set prior to this call are ctx.saltlen and
+* ctx.outlen (which must be the maximal salt and out length values that are
+* allowed), ctx.salt and ctx.out (which must be buffers of the specified
+* length), and ctx.pwd and ctx.pwdlen which must hold a valid password.
+*
+* Invalid input string causes an error. On success, the ctx is valid and all
+* fields have been initialized.
+*
+* Returned value is ARGON2_OK on success, other ARGON2_ codes on error.
+*/
+int decode_string(argon2_context *ctx, const char *str, argon2_type type);
+
+/* Returns the length of the encoded byte stream with length len */
+size_t b64len(uint32_t len);
+
+/* Returns the length of the encoded number num */
+size_t numlen(uint32_t num);
+
+#endif
diff --git a/src/argon2-impl-select.c b/src/argon2-impl-select.c
new file mode 100644
index 0000000..b4c18a7
--- /dev/null
+++ b/src/argon2-impl-select.c
@@ -0,0 +1,93 @@
+#include <time.h>
+#include <string.h>
+
+#include "argon2-impl-select.h"
+
+#include "argon2.h"
+
+#define BENCH_SAMPLES 512
+#define BENCH_MEM_BLOCKS 512
+
+static argon2_impl selected_argon_impl = {
+ "(default)", NULL, fill_segment_default
+};
+
+/* the benchmark routine is not thread-safe, so we can use a global var here: */
+static block memory[BENCH_MEM_BLOCKS];
+
+static uint64_t benchmark_impl(const argon2_impl *impl) {
+ clock_t time;
+ unsigned int i;
+ uint64_t bench;
+ argon2_instance_t instance;
+ argon2_position_t pos;
+
+ memset(memory, 0, sizeof(memory));
+
+ instance.version = ARGON2_VERSION_NUMBER;
+ instance.memory = memory;
+ instance.passes = 1;
+ instance.memory_blocks = BENCH_MEM_BLOCKS;
+ instance.segment_length = BENCH_MEM_BLOCKS / ARGON2_SYNC_POINTS;
+ instance.lane_length = instance.segment_length * ARGON2_SYNC_POINTS;
+ instance.lanes = 1;
+ instance.threads = 1;
+ instance.type = Argon2_i;
+
+ pos.lane = 0;
+ pos.pass = 0;
+ pos.slice = 0;
+ pos.index = 0;
+
+ /* warm-up cache: */
+ impl->fill_segment(&instance, pos);
+
+ /* OK, now measure: */
+ bench = 0;
+ time = clock();
+ for (i = 0; i < BENCH_SAMPLES; i++) {
+ impl->fill_segment(&instance, pos);
+ }
+ time = clock() - time;
+ bench = (uint64_t)time;
+ return bench;
+}
+
+static void select_impl(FILE *out, const char *prefix)
+{
+ argon2_impl_list impls;
+ unsigned int i;
+ const argon2_impl *best_impl = NULL;
+ uint64_t best_bench = UINT_MAX;
+
+ argon2_get_impl_list(&impls);
+
+ for (i = 0; i < impls.count; i++) {
+ const argon2_impl *impl = &impls.entries[i];
+ uint64_t bench;
+
+ bench = benchmark_impl(impl);
+
+ if (bench < best_bench) {
+ best_bench = bench;
+ best_impl = impl;
+ }
+ }
+
+ if (best_impl != NULL) {
+ selected_argon_impl = *best_impl;
+ }
+}
+
+void fill_segment(const argon2_instance_t *instance, argon2_position_t position)
+{
+ selected_argon_impl.fill_segment(instance, position);
+}
+
+void argon2_select_impl(FILE *out, const char *prefix)
+{
+ if (prefix == NULL) {
+ prefix = "";
+ }
+ select_impl(out, prefix);
+}
diff --git a/src/argon2-impl-select.h b/src/argon2-impl-select.h
new file mode 100644
index 0000000..29ba47c
--- /dev/null
+++ b/src/argon2-impl-select.h
@@ -0,0 +1,23 @@
+#ifndef ARGON2_IMPL_SELECT_H
+#define ARGON2_IMPL_SELECT_H
+
+#include "argon2-core.h"
+
+typedef struct Argon2_impl {
+ const char *name;
+ int (*check)(void);
+ void (*fill_segment)(const argon2_instance_t *instance,
+ argon2_position_t position);
+} argon2_impl;
+
+typedef struct Argon2_impl_list {
+ const argon2_impl *entries;
+ size_t count;
+} argon2_impl_list;
+
+void argon2_get_impl_list(argon2_impl_list *list);
+void fill_segment_default(const argon2_instance_t *instance,
+ argon2_position_t position);
+
+#endif // ARGON2_IMPL_SELECT_H
+
diff --git a/src/argon2-template-64.h b/src/argon2-template-64.h
new file mode 100644
index 0000000..b07c215
--- /dev/null
+++ b/src/argon2-template-64.h
@@ -0,0 +1,193 @@
+#include <string.h>
+
+#include "argon2-core.h"
+
+#define MASK_32 UINT64_C(0xFFFFFFFF)
+
+#define F(x, y) ((x) + (y) + 2 * ((x) & MASK_32) * ((y) & MASK_32))
+
+#define G(a, b, c, d) \
+ do { \
+ a = F(a, b); \
+ d = rotr64(d ^ a, 32); \
+ c = F(c, d); \
+ b = rotr64(b ^ c, 24); \
+ a = F(a, b); \
+ d = rotr64(d ^ a, 16); \
+ c = F(c, d); \
+ b = rotr64(b ^ c, 63); \
+ } while ((void)0, 0)
+
+#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, \
+ v8, v9, v10, v11, v12, v13, v14, v15) \
+ do { \
+ G(v0, v4, v8, v12); \
+ G(v1, v5, v9, v13); \
+ G(v2, v6, v10, v14); \
+ G(v3, v7, v11, v15); \
+ G(v0, v5, v10, v15); \
+ G(v1, v6, v11, v12); \
+ G(v2, v7, v8, v13); \
+ G(v3, v4, v9, v14); \
+ } while ((void)0, 0)
+
+#define BLAKE2_ROUND_NOMSG1(v) \
+ BLAKE2_ROUND_NOMSG( \
+ (v)[ 0], (v)[ 1], (v)[ 2], (v)[ 3], \
+ (v)[ 4], (v)[ 5], (v)[ 6], (v)[ 7], \
+ (v)[ 8], (v)[ 9], (v)[10], (v)[11], \
+ (v)[12], (v)[13], (v)[14], (v)[15])
+
+#define BLAKE2_ROUND_NOMSG2(v) \
+ BLAKE2_ROUND_NOMSG( \
+ (v)[ 0], (v)[ 1], (v)[ 16], (v)[ 17], \
+ (v)[ 32], (v)[ 33], (v)[ 48], (v)[ 49], \
+ (v)[ 64], (v)[ 65], (v)[ 80], (v)[ 81], \
+ (v)[ 96], (v)[ 97], (v)[112], (v)[113])
+
+static void fill_block(const block *prev_block, const block *ref_block,
+ block *next_block, int with_xor)
+{
+ block blockR, block_tmp;
+
+ copy_block(&blockR, ref_block);
+ xor_block(&blockR, prev_block);
+ copy_block(&block_tmp, &blockR);
+ if (with_xor) {
+ xor_block(&block_tmp, next_block);
+ }
+
+ /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
+ (16,17,..31)... finally (112,113,...127) */
+ BLAKE2_ROUND_NOMSG1(blockR.v + 0 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 1 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 2 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 3 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 4 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 5 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 6 * 16);
+ BLAKE2_ROUND_NOMSG1(blockR.v + 7 * 16);
+
+ /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
+ (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
+ BLAKE2_ROUND_NOMSG2(blockR.v + 0 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 1 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 2 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 3 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 4 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 5 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 6 * 2);
+ BLAKE2_ROUND_NOMSG2(blockR.v + 7 * 2);
+
+ copy_block(next_block, &block_tmp);
+ xor_block(next_block, &blockR);
+}
+
+static void next_addresses(block *address_block, block *input_block,
+ const block *zero_block)
+{
+ input_block->v[6]++;
+ fill_block(zero_block, input_block, address_block, 0);
+ fill_block(zero_block, address_block, address_block, 0);
+}
+
+static void fill_segment_64(const argon2_instance_t *instance,
+ argon2_position_t position)
+{
+ block *ref_block, *curr_block, *prev_block;
+ block address_block, input_block, zero_block;
+ uint64_t pseudo_rand, ref_index, ref_lane;
+ uint32_t prev_offset, curr_offset;
+ uint32_t starting_index, i;
+ int data_independent_addressing;
+
+ if (instance == NULL) {
+ return;
+ }
+
+ data_independent_addressing = (instance->type == Argon2_i) ||
+ (instance->type == Argon2_id && (position.pass == 0) &&
+ (position.slice < ARGON2_SYNC_POINTS / 2));
+
+ if (data_independent_addressing) {
+ init_block_value(&zero_block, 0);
+ init_block_value(&input_block, 0);
+
+ input_block.v[0] = position.pass;
+ input_block.v[1] = position.lane;
+ input_block.v[2] = position.slice;
+ input_block.v[3] = instance->memory_blocks;
+ input_block.v[4] = instance->passes;
+ input_block.v[5] = instance->type;
+ }
+
+ starting_index = 0;
+
+ if ((0 == position.pass) && (0 == position.slice)) {
+ starting_index = 2; /* we have already generated the first two blocks */
+
+ /* Don't forget to generate the first block of addresses: */
+ if (data_independent_addressing) {
+ next_addresses(&address_block, &input_block, &zero_block);
+ }
+ }
+
+ /* Offset of the current block */
+ curr_offset = position.lane * instance->lane_length +
+ position.slice * instance->segment_length + starting_index;
+
+ if (0 == curr_offset % instance->lane_length) {
+ /* Last block in this lane */
+ prev_offset = curr_offset + instance->lane_length - 1;
+ } else {
+ /* Previous block */
+ prev_offset = curr_offset - 1;
+ }
+
+ for (i = starting_index; i < instance->segment_length;
+ ++i, ++curr_offset, ++prev_offset) {
+ /*1.1 Rotating prev_offset if needed */
+ if (curr_offset % instance->lane_length == 1) {
+ prev_offset = curr_offset - 1;
+ }
+
+ /* 1.2 Computing the index of the reference block */
+ /* 1.2.1 Taking pseudo-random value from the previous block */
+ if (data_independent_addressing) {
+ if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
+ next_addresses(&address_block, &input_block, &zero_block);
+ }
+ pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
+ } else {
+ pseudo_rand = instance->memory[prev_offset].v[0];
+ }
+
+ /* 1.2.2 Computing the lane of the reference block */
+ ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
+
+ if ((position.pass == 0) && (position.slice == 0)) {
+ /* Can not reference other lanes yet */
+ ref_lane = position.lane;
+ }
+
+ /* 1.2.3 Computing the number of possible reference block within the
+ * lane.
+ */
+ position.index = i;
+ ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
+ ref_lane == position.lane);
+
+ /* 2 Creating a new block */
+ ref_block =
+ instance->memory + instance->lane_length * ref_lane + ref_index;
+ curr_block = instance->memory + curr_offset;
+ prev_block = instance->memory + prev_offset;
+
+ /* version 1.2.1 and earlier: overwrite, not XOR */
+ if (0 == position.pass || ARGON2_VERSION_10 == instance->version) {
+ fill_block(prev_block, ref_block, curr_block, 0);
+ } else {
+ fill_block(prev_block, ref_block, curr_block, 1);
+ }
+ }
+}
diff --git a/src/argon2-thread.c b/src/argon2-thread.c
new file mode 100644
index 0000000..b554ae7
--- /dev/null
+++ b/src/argon2-thread.c
@@ -0,0 +1,36 @@
+#include "argon2-thread.h"
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+int argon2_thread_create(argon2_thread_handle_t *handle,
+ argon2_thread_func_t func, void *args) {
+ if (NULL == handle || func == NULL) {
+ return -1;
+ }
+#if defined(_WIN32)
+ *handle = _beginthreadex(NULL, 0, func, args, 0, NULL);
+ return *handle != 0 ? 0 : -1;
+#else
+ return pthread_create(handle, NULL, func, args);
+#endif
+}
+
+int argon2_thread_join(argon2_thread_handle_t handle) {
+#if defined(_WIN32)
+ if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) {
+ return CloseHandle((HANDLE)handle) != 0 ? 0 : -1;
+ }
+ return -1;
+#else
+ return pthread_join(handle, NULL);
+#endif
+}
+
+void argon2_thread_exit(void) {
+#if defined(_WIN32)
+ _endthreadex(0);
+#else
+ pthread_exit(NULL);
+#endif
+}
diff --git a/src/argon2-thread.h b/src/argon2-thread.h
new file mode 100644
index 0000000..f1ef519
--- /dev/null
+++ b/src/argon2-thread.h
@@ -0,0 +1,47 @@
+#ifndef ARGON2_THREAD_H
+#define ARGON2_THREAD_H
+/*
+ Here we implement an abstraction layer for the simpĺe requirements
+ of the Argon2 code. We only require 3 primitives---thread creation,
+ joining, and termination---so full emulation of the pthreads API
+ is unwarranted. Currently we wrap pthreads and Win32 threads.
+
+ The API defines 2 types: the function pointer type,
+ argon2_thread_func_t,
+ and the type of the thread handle---argon2_thread_handle_t.
+*/
+#if defined(_WIN32)
+#include <process.h>
+#include <stdint.h>
+typedef unsigned(__stdcall *argon2_thread_func_t)(void *);
+typedef uintptr_t argon2_thread_handle_t;
+#else
+#include <pthread.h>
+typedef void *(*argon2_thread_func_t)(void *);
+typedef pthread_t argon2_thread_handle_t;
+#endif
+
+/* Creates a thread
+ * @param handle pointer to a thread handle, which is the output of this
+ * function. Must not be NULL.
+ * @param func A function pointer for the thread's entry point. Must not be
+ * NULL.
+ * @param args Pointer that is passed as an argument to @func. May be NULL.
+ * @return 0 if @handle and @func are valid pointers and a thread is successfuly
+ * created.
+ */
+int argon2_thread_create(argon2_thread_handle_t *handle,
+ argon2_thread_func_t func, void *args);
+
+/* Waits for a thread to terminate
+ * @param handle Handle to a thread created with argon2_thread_create.
+ * @return 0 if @handle is a valid handle, and joining completed successfully.
+*/
+int argon2_thread_join(argon2_thread_handle_t handle);
+
+/* Terminate the current thread. Must be run inside a thread created by
+ * argon2_thread_create.
+*/
+void argon2_thread_exit(void);
+
+#endif
diff --git a/src/argon2.c b/src/argon2.c
new file mode 100644
index 0000000..0f26040
--- /dev/null
+++ b/src/argon2.c
@@ -0,0 +1,476 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with
+ * this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "argon2.h"
+#include "argon2-encoding.h"
+#include "argon2-core.h"
+
+const char *argon2_type2string(argon2_type type, int uppercase) {
+ switch (type) {
+ case Argon2_d:
+ return uppercase ? "Argon2d" : "argon2d";
+ case Argon2_i:
+ return uppercase ? "Argon2i" : "argon2i";
+ case Argon2_id:
+ return uppercase ? "Argon2id" : "argon2id";
+ }
+
+ return NULL;
+}
+
+static void argon2_compute_memory_blocks(uint32_t *memory_blocks,
+ uint32_t *segment_length,
+ uint32_t m_cost, uint32_t lanes)
+{
+ /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
+ *memory_blocks = m_cost;
+ if (*memory_blocks < 2 * ARGON2_SYNC_POINTS * lanes) {
+ *memory_blocks = 2 * ARGON2_SYNC_POINTS * lanes;
+ }
+
+ *segment_length = *memory_blocks / (lanes * ARGON2_SYNC_POINTS);
+ /* Ensure that all segments have equal length */
+ *memory_blocks = *segment_length * (lanes * ARGON2_SYNC_POINTS);
+}
+
+size_t argon2_memory_size(uint32_t m_cost, uint32_t parallelism) {
+ uint32_t memory_blocks, segment_length;
+ argon2_compute_memory_blocks(&memory_blocks, &segment_length, m_cost,
+ parallelism);
+ return memory_blocks * ARGON2_BLOCK_SIZE;
+}
+
+int argon2_ctx_mem(argon2_context *context, argon2_type type, void *memory,
+ size_t memory_size) {
+ /* 1. Validate all inputs */
+ int result = validate_inputs(context);
+ uint32_t memory_blocks, segment_length;
+ argon2_instance_t instance;
+
+ if (ARGON2_OK != result) {
+ return result;
+ }
+
+ if (Argon2_d != type && Argon2_i != type && Argon2_id != type) {
+ return ARGON2_INCORRECT_TYPE;
+ }
+
+ /* 2. Align memory size */
+ argon2_compute_memory_blocks(&memory_blocks, &segment_length,
+ context->m_cost, context->lanes);
+
+ /* check for sufficient memory size: */
+ if (memory != NULL && (memory_size % ARGON2_BLOCK_SIZE != 0 ||
+ memory_size / ARGON2_BLOCK_SIZE < memory_blocks)) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ instance.version = context->version;
+ instance.memory = (block *)memory;
+ instance.passes = context->t_cost;
+ instance.memory_blocks = memory_blocks;
+ instance.segment_length = segment_length;
+ instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
+ instance.lanes = context->lanes;
+ instance.threads = context->threads;
+ instance.type = type;
+ instance.print_internals = !!(context->flags & ARGON2_FLAG_GENKAT);
+ instance.keep_memory = memory != NULL;
+
+ if (instance.threads > instance.lanes) {
+ instance.threads = instance.lanes;
+ }
+
+ /* 3. Initialization: Hashing inputs, allocating memory, filling first
+ * blocks
+ */
+ result = initialize(&instance, context);
+
+ if (ARGON2_OK != result) {
+ return result;
+ }
+
+ /* 4. Filling memory */
+ result = fill_memory_blocks(&instance);
+
+ if (ARGON2_OK != result) {
+ return result;
+ }
+ /* 5. Finalization */
+ finalize(context, &instance);
+
+ return ARGON2_OK;
+}
+
+int argon2_ctx(argon2_context *context, argon2_type type) {
+ return argon2_ctx_mem(context, type, NULL, 0);
+}
+
+int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt, const size_t saltlen,
+ void *hash, const size_t hashlen, char *encoded,
+ const size_t encodedlen, argon2_type type,
+ const uint32_t version){
+
+ argon2_context context;
+ int result;
+ uint8_t *out;
+
+ if (pwdlen > ARGON2_MAX_PWD_LENGTH) {
+ return ARGON2_PWD_TOO_LONG;
+ }
+
+ if (saltlen > ARGON2_MAX_SALT_LENGTH) {
+ return ARGON2_SALT_TOO_LONG;
+ }
+
+ if (hashlen > ARGON2_MAX_OUTLEN) {
+ return ARGON2_OUTPUT_TOO_LONG;
+ }
+
+ if (hashlen < ARGON2_MIN_OUTLEN) {
+ return ARGON2_OUTPUT_TOO_SHORT;
+ }
+
+ out = malloc(hashlen);
+ if (!out) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ context.out = (uint8_t *)out;
+ context.outlen = (uint32_t)hashlen;
+ context.pwd = CONST_CAST(uint8_t *)pwd;
+ context.pwdlen = (uint32_t)pwdlen;
+ context.salt = CONST_CAST(uint8_t *)salt;
+ context.saltlen = (uint32_t)saltlen;
+ context.secret = NULL;
+ context.secretlen = 0;
+ context.ad = NULL;
+ context.adlen = 0;
+ context.t_cost = t_cost;
+ context.m_cost = m_cost;
+ context.lanes = parallelism;
+ context.threads = parallelism;
+ context.allocate_cbk = NULL;
+ context.free_cbk = NULL;
+ context.flags = ARGON2_DEFAULT_FLAGS;
+ context.version = version;
+
+ result = argon2_ctx(&context, type);
+
+ if (result != ARGON2_OK) {
+ clear_internal_memory(out, hashlen);
+ free(out);
+ return result;
+ }
+
+ /* if raw hash requested, write it */
+ if (hash) {
+ memcpy(hash, out, hashlen);
+ }
+
+ /* if encoding requested, write it */
+ if (encoded && encodedlen) {
+ if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) {
+ clear_internal_memory(out, hashlen); /* wipe buffers if error */
+ clear_internal_memory(encoded, encodedlen);
+ free(out);
+ return ARGON2_ENCODING_FAIL;
+ }
+ }
+ clear_internal_memory(out, hashlen);
+ free(out);
+
+ return ARGON2_OK;
+}
+
+int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, const size_t hashlen,
+ char *encoded, const size_t encodedlen) {
+
+ return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
+ NULL, hashlen, encoded, encodedlen, Argon2_i,
+ ARGON2_VERSION_NUMBER);
+}
+
+int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash, const size_t hashlen) {
+
+ return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
+ hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER);
+}
+
+int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, const size_t hashlen,
+ char *encoded, const size_t encodedlen) {
+
+ return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
+ NULL, hashlen, encoded, encodedlen, Argon2_d,
+ ARGON2_VERSION_NUMBER);
+}
+
+int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash, const size_t hashlen) {
+
+ return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
+ hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER);
+}
+
+int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, const size_t hashlen,
+ char *encoded, const size_t encodedlen) {
+
+ return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
+ NULL, hashlen, encoded, encodedlen, Argon2_id,
+ ARGON2_VERSION_NUMBER);
+}
+
+int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash, const size_t hashlen) {
+ return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
+ hash, hashlen, NULL, 0, Argon2_id,
+ ARGON2_VERSION_NUMBER);
+}
+
+static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) {
+ size_t i;
+ uint8_t d = 0U;
+
+ for (i = 0U; i < len; i++) {
+ d |= b1[i] ^ b2[i];
+ }
+ return (int)((1 & ((d - 1) >> 8)) - 1);
+}
+
+int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen,
+ argon2_type type) {
+
+ argon2_context ctx;
+ uint8_t *desired_result = NULL;
+
+ int ret = ARGON2_OK;
+
+ size_t encoded_len;
+ uint32_t max_field_len;
+
+ if (pwdlen > ARGON2_MAX_PWD_LENGTH) {
+ return ARGON2_PWD_TOO_LONG;
+ }
+
+ if (encoded == NULL) {
+ return ARGON2_DECODING_FAIL;
+ }
+
+ encoded_len = strlen(encoded);
+ if (encoded_len > UINT32_MAX) {
+ return ARGON2_DECODING_FAIL;
+ }
+
+ /* No field can be longer than the encoded length */
+ max_field_len = (uint32_t)encoded_len;
+
+ ctx.saltlen = max_field_len;
+ ctx.outlen = max_field_len;
+
+ ctx.salt = malloc(ctx.saltlen);
+ ctx.out = malloc(ctx.outlen);
+ if (!ctx.salt || !ctx.out) {
+ ret = ARGON2_MEMORY_ALLOCATION_ERROR;
+ goto fail;
+ }
+
+ ctx.pwd = (uint8_t *)pwd;
+ ctx.pwdlen = (uint32_t)pwdlen;
+
+ ret = decode_string(&ctx, encoded, type);
+ if (ret != ARGON2_OK) {
+ goto fail;
+ }
+
+ /* Set aside the desired result, and get a new buffer. */
+ desired_result = ctx.out;
+ ctx.out = malloc(ctx.outlen);
+ if (!ctx.out) {
+ ret = ARGON2_MEMORY_ALLOCATION_ERROR;
+ goto fail;
+ }
+
+ ret = argon2_verify_ctx(&ctx, (char *)desired_result, type);
+ if (ret != ARGON2_OK) {
+ goto fail;
+ }
+
+fail:
+ free(ctx.salt);
+ free(ctx.out);
+ free(desired_result);
+
+ return ret;
+}
+
+int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
+
+ return argon2_verify(encoded, pwd, pwdlen, Argon2_i);
+}
+
+int argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
+
+ return argon2_verify(encoded, pwd, pwdlen, Argon2_d);
+}
+
+int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
+
+ return argon2_verify(encoded, pwd, pwdlen, Argon2_id);
+}
+
+int argon2d_ctx(argon2_context *context) {
+ return argon2_ctx(context, Argon2_d);
+}
+
+int argon2i_ctx(argon2_context *context) {
+ return argon2_ctx(context, Argon2_i);
+}
+
+int argon2id_ctx(argon2_context *context) {
+ return argon2_ctx(context, Argon2_id);
+}
+
+int argon2_verify_ctx(argon2_context *context, const char *hash,
+ argon2_type type) {
+ int ret = argon2_ctx(context, type);
+ if (ret != ARGON2_OK) {
+ return ret;
+ }
+
+ if (argon2_compare((uint8_t *)hash, context->out, context->outlen)) {
+ return ARGON2_VERIFY_MISMATCH;
+ }
+
+ return ARGON2_OK;
+}
+
+int argon2d_verify_ctx(argon2_context *context, const char *hash) {
+ return argon2_verify_ctx(context, hash, Argon2_d);
+}
+
+int argon2i_verify_ctx(argon2_context *context, const char *hash) {
+ return argon2_verify_ctx(context, hash, Argon2_i);
+}
+
+int argon2id_verify_ctx(argon2_context *context, const char *hash) {
+ return argon2_verify_ctx(context, hash, Argon2_id);
+}
+
+const char *argon2_error_message(int error_code) {
+ switch (error_code) {
+ case ARGON2_OK:
+ return "OK";
+ case ARGON2_OUTPUT_PTR_NULL:
+ return "Output pointer is NULL";
+ case ARGON2_OUTPUT_TOO_SHORT:
+ return "Output is too short";
+ case ARGON2_OUTPUT_TOO_LONG:
+ return "Output is too long";
+ case ARGON2_PWD_TOO_SHORT:
+ return "Password is too short";
+ case ARGON2_PWD_TOO_LONG:
+ return "Password is too long";
+ case ARGON2_SALT_TOO_SHORT:
+ return "Salt is too short";
+ case ARGON2_SALT_TOO_LONG:
+ return "Salt is too long";
+ case ARGON2_AD_TOO_SHORT:
+ return "Associated data is too short";
+ case ARGON2_AD_TOO_LONG:
+ return "Associated data is too long";
+ case ARGON2_SECRET_TOO_SHORT:
+ return "Secret is too short";
+ case ARGON2_SECRET_TOO_LONG:
+ return "Secret is too long";
+ case ARGON2_TIME_TOO_SMALL:
+ return "Time cost is too small";
+ case ARGON2_TIME_TOO_LARGE:
+ return "Time cost is too large";
+ case ARGON2_MEMORY_TOO_LITTLE:
+ return "Memory cost is too small";
+ case ARGON2_MEMORY_TOO_MUCH:
+ return "Memory cost is too large";
+ case ARGON2_LANES_TOO_FEW:
+ return "Too few lanes";
+ case ARGON2_LANES_TOO_MANY:
+ return "Too many lanes";
+ case ARGON2_PWD_PTR_MISMATCH:
+ return "Password pointer is NULL, but password length is not 0";
+ case ARGON2_SALT_PTR_MISMATCH:
+ return "Salt pointer is NULL, but salt length is not 0";
+ case ARGON2_SECRET_PTR_MISMATCH:
+ return "Secret pointer is NULL, but secret length is not 0";
+ case ARGON2_AD_PTR_MISMATCH:
+ return "Associated data pointer is NULL, but ad length is not 0";
+ case ARGON2_MEMORY_ALLOCATION_ERROR:
+ return "Memory allocation error";
+ case ARGON2_FREE_MEMORY_CBK_NULL:
+ return "The free memory callback is NULL";
+ case ARGON2_ALLOCATE_MEMORY_CBK_NULL:
+ return "The allocate memory callback is NULL";
+ case ARGON2_INCORRECT_PARAMETER:
+ return "Argon2_Context context is NULL";
+ case ARGON2_INCORRECT_TYPE:
+ return "There is no such version of Argon2";
+ case ARGON2_OUT_PTR_MISMATCH:
+ return "Output pointer mismatch";
+ case ARGON2_THREADS_TOO_FEW:
+ return "Not enough threads";
+ case ARGON2_THREADS_TOO_MANY:
+ return "Too many threads";
+ case ARGON2_MISSING_ARGS:
+ return "Missing arguments";
+ case ARGON2_ENCODING_FAIL:
+ return "Encoding failed";
+ case ARGON2_DECODING_FAIL:
+ return "Decoding failed";
+ case ARGON2_THREAD_FAIL:
+ return "Threading failure";
+ case ARGON2_DECODING_LENGTH_FAIL:
+ return "Some of encoded parameters are too long or too short";
+ case ARGON2_VERIFY_MISMATCH:
+ return "The password does not match the supplied hash";
+ default:
+ return "Unknown error code";
+ }
+}
+
+size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism,
+ uint32_t saltlen, uint32_t hashlen, argon2_type type) {
+ return strlen("$$v=$m=,t=,p=$$") + strlen(argon2_type2string(type, 0)) +
+ numlen(t_cost) + numlen(m_cost) + numlen(parallelism) +
+ b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) +
+ 1;
+}
diff --git a/src/argon2.h b/src/argon2.h
new file mode 100644
index 0000000..c526aa8
--- /dev/null
+++ b/src/argon2.h
@@ -0,0 +1,465 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication
+ * along with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef ARGON2_H
+#define ARGON2_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <limits.h>
+
+/* Symbols visibility control */
+#ifdef A2_VISCTL
+#define ARGON2_PUBLIC __attribute__((visibility("default")))
+#elif _MSC_VER
+#ifdef argon2_EXPORTS
+#define ARGON2_PUBLIC __declspec(dllexport)
+#else
+#define ARGON2_PUBLIC __declspec(dllimport)
+#endif
+#else
+#define ARGON2_PUBLIC
+#endif // A2_VISCTL
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Argon2 input parameter restrictions
+ */
+
+/* Minimum and maximum number of lanes (degree of parallelism) */
+#define ARGON2_MIN_LANES UINT32_C(1)
+#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF)
+
+/* Minimum and maximum number of threads */
+#define ARGON2_MIN_THREADS UINT32_C(1)
+#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF)
+
+/* Number of synchronization points between lanes per pass */
+#define ARGON2_SYNC_POINTS UINT32_C(4)
+
+/* Minimum and maximum digest size in bytes */
+#define ARGON2_MIN_OUTLEN UINT32_C(4)
+#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
+#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
+
+#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
+/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
+#define ARGON2_MAX_MEMORY_BITS \
+ ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1))
+#define ARGON2_MAX_MEMORY \
+ ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
+
+/* Minimum and maximum number of passes */
+#define ARGON2_MIN_TIME UINT32_C(1)
+#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum password length in bytes */
+#define ARGON2_MIN_PWD_LENGTH UINT32_C(0)
+#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum associated data length in bytes */
+#define ARGON2_MIN_AD_LENGTH UINT32_C(0)
+#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum salt length in bytes */
+#define ARGON2_MIN_SALT_LENGTH UINT32_C(8)
+#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum key length in bytes */
+#define ARGON2_MIN_SECRET UINT32_C(0)
+#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF)
+
+/* Flags to determine which fields are securely wiped (default = no wipe). */
+#define ARGON2_DEFAULT_FLAGS UINT32_C(0)
+#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0)
+#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1)
+#define ARGON2_FLAG_GENKAT (UINT32_C(1) << 3)
+
+/* Global flag to determine if we are wiping internal memory buffers. This flag
+ * is defined in core.c and deafults to 1 (wipe internal memory). */
+extern int FLAG_clear_internal_memory;
+
+/* Error codes */
+typedef enum Argon2_ErrorCodes {
+ ARGON2_OK = 0,
+
+ ARGON2_OUTPUT_PTR_NULL = -1,
+
+ ARGON2_OUTPUT_TOO_SHORT = -2,
+ ARGON2_OUTPUT_TOO_LONG = -3,
+
+ ARGON2_PWD_TOO_SHORT = -4,
+ ARGON2_PWD_TOO_LONG = -5,
+
+ ARGON2_SALT_TOO_SHORT = -6,
+ ARGON2_SALT_TOO_LONG = -7,
+
+ ARGON2_AD_TOO_SHORT = -8,
+ ARGON2_AD_TOO_LONG = -9,
+
+ ARGON2_SECRET_TOO_SHORT = -10,
+ ARGON2_SECRET_TOO_LONG = -11,
+
+ ARGON2_TIME_TOO_SMALL = -12,
+ ARGON2_TIME_TOO_LARGE = -13,
+
+ ARGON2_MEMORY_TOO_LITTLE = -14,
+ ARGON2_MEMORY_TOO_MUCH = -15,
+
+ ARGON2_LANES_TOO_FEW = -16,
+ ARGON2_LANES_TOO_MANY = -17,
+
+ ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */
+ ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */
+ ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
+ ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */
+
+ ARGON2_MEMORY_ALLOCATION_ERROR = -22,
+
+ ARGON2_FREE_MEMORY_CBK_NULL = -23,
+ ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
+
+ ARGON2_INCORRECT_PARAMETER = -25,
+ ARGON2_INCORRECT_TYPE = -26,
+
+ ARGON2_OUT_PTR_MISMATCH = -27,
+
+ ARGON2_THREADS_TOO_FEW = -28,
+ ARGON2_THREADS_TOO_MANY = -29,
+
+ ARGON2_MISSING_ARGS = -30,
+
+ ARGON2_ENCODING_FAIL = -31,
+
+ ARGON2_DECODING_FAIL = -32,
+
+ ARGON2_THREAD_FAIL = -33,
+
+ ARGON2_DECODING_LENGTH_FAIL = -34,
+
+ ARGON2_VERIFY_MISMATCH = -35
+} argon2_error_codes;
+
+/* Memory allocator types --- for external allocation */
+typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
+typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
+
+/* Argon2 external data structures */
+
+/*
+ *****
+ * Context: structure to hold Argon2 inputs:
+ * output array and its length,
+ * password and its length,
+ * salt and its length,
+ * secret and its length,
+ * associated data and its length,
+ * number of passes, amount of used memory (in KBytes, can be rounded up a bit)
+ * number of parallel threads that will be run.
+ * All the parameters above affect the output hash value.
+ * Additionally, two function pointers can be provided to allocate and
+ * deallocate the memory (if NULL, memory will be allocated internally).
+ * Also, three flags indicate whether to erase password, secret as soon as they
+ * are pre-hashed (and thus not needed anymore), and the entire memory
+ *****
+ * Simplest situation: you have output array out[8], password is stored in
+ * pwd[32], salt is stored in salt[16], you do not have keys nor associated
+ * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
+ * 4 parallel lanes.
+ * You want to erase the password, but you're OK with last pass not being
+ * erased. You want to use the default memory allocator.
+ * Then you initialize:
+ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
+ */
+typedef struct Argon2_Context {
+ uint8_t *out; /* output array */
+ uint32_t outlen; /* digest length */
+
+ uint8_t *pwd; /* password array */
+ uint32_t pwdlen; /* password length */
+
+ uint8_t *salt; /* salt array */
+ uint32_t saltlen; /* salt length */
+
+ uint8_t *secret; /* key array */
+ uint32_t secretlen; /* key length */
+
+ uint8_t *ad; /* associated data array */
+ uint32_t adlen; /* associated data length */
+
+ uint32_t t_cost; /* number of passes */
+ uint32_t m_cost; /* amount of memory requested (KB) */
+ uint32_t lanes; /* number of lanes */
+ uint32_t threads; /* maximum number of threads */
+
+ uint32_t version; /* version number */
+
+ allocate_fptr allocate_cbk; /* pointer to memory allocator */
+ deallocate_fptr free_cbk; /* pointer to memory deallocator */
+
+ uint32_t flags; /* array of bool options */
+} argon2_context;
+
+/* Argon2 primitive type */
+typedef enum Argon2_type {
+ Argon2_d = 0,
+ Argon2_i = 1,
+ Argon2_id = 2
+} argon2_type;
+
+/* Version of the algorithm */
+typedef enum Argon2_version {
+ ARGON2_VERSION_10 = 0x10,
+ ARGON2_VERSION_13 = 0x13,
+ ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
+} argon2_version;
+
+/*
+ * Function that gives the string representation of an argon2_type.
+ * @param type The argon2_type that we want the string for
+ * @param uppercase Whether the string should have the first letter uppercase
+ * @return NULL if invalid type, otherwise the string representation.
+ */
+ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase);
+
+/*
+ * Function that performs memory-hard hashing with certain degree of parallelism
+ * @param context Pointer to the Argon2 internal structure
+ * @return Error code if smth is wrong, ARGON2_OK otherwise
+ */
+ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type);
+
+/**
+ * Hashes a password with Argon2i, producing an encoded hash
+ * @param t_cost Number of iterations
+ * @param m_cost Sets memory usage to m_cost kibibytes
+ * @param parallelism Number of threads and compute lanes
+ * @param pwd Pointer to password
+ * @param pwdlen Password size in bytes
+ * @param salt Pointer to salt
+ * @param saltlen Salt size in bytes
+ * @param hashlen Desired length of the hash in bytes
+ * @param encoded Buffer where to write the encoded hash
+ * @param encodedlen Size of the buffer (thus max size of the encoded hash)
+ * @pre Different parallelism levels will give different results
+ * @pre Returns ARGON2_OK if successful
+ */
+ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost,
+ const uint32_t m_cost,
+ const uint32_t parallelism,
+ const void *pwd, const size_t pwdlen,
+ const void *salt, const size_t saltlen,
+ const size_t hashlen, char *encoded,
+ const size_t encodedlen);
+
+/**
+ * Hashes a password with Argon2i, producing a raw hash by allocating memory at
+ * @hash
+ * @param t_cost Number of iterations
+ * @param m_cost Sets memory usage to m_cost kibibytes
+ * @param parallelism Number of threads and compute lanes
+ * @param pwd Pointer to password
+ * @param pwdlen Password size in bytes
+ * @param salt Pointer to salt
+ * @param saltlen Salt size in bytes
+ * @param hash Buffer where to write the raw hash - updated by the function
+ * @param hashlen Desired length of the hash in bytes
+ * @pre Different parallelism levels will give different results
+ * @pre Returns ARGON2_OK if successful
+ */
+ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash,
+ const size_t hashlen);
+
+ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost,
+ const uint32_t m_cost,
+ const uint32_t parallelism,
+ const void *pwd, const size_t pwdlen,
+ const void *salt, const size_t saltlen,
+ const size_t hashlen, char *encoded,
+ const size_t encodedlen);
+
+ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost,
+ const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash,
+ const size_t hashlen);
+
+ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost,
+ const uint32_t m_cost,
+ const uint32_t parallelism,
+ const void *pwd, const size_t pwdlen,
+ const void *salt, const size_t saltlen,
+ const size_t hashlen, char *encoded,
+ const size_t encodedlen);
+
+ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost,
+ const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash,
+ const size_t hashlen);
+
+/* generic function underlying the above ones */
+ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
+ const uint32_t parallelism, const void *pwd,
+ const size_t pwdlen, const void *salt,
+ const size_t saltlen, void *hash,
+ const size_t hashlen, char *encoded,
+ const size_t encodedlen, argon2_type type,
+ const uint32_t version);
+
+/**
+ * Verifies a password against an encoded string
+ * Encoded string is restricted as in validate_inputs()
+ * @param encoded String encoding parameters, salt, hash
+ * @param pwd Pointer to password
+ * @pre Returns ARGON2_OK if successful
+ */
+ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd,
+ const size_t pwdlen);
+
+ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd,
+ const size_t pwdlen);
+
+ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd,
+ const size_t pwdlen);
+
+/* generic function underlying the above ones */
+ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd,
+ const size_t pwdlen, argon2_type type);
+
+/**
+ * Argon2d: Version of Argon2 that picks memory blocks depending
+ * on the password and salt. Only for side-channel-free
+ * environment!!
+ *****
+ * @param context Pointer to current Argon2 context
+ * @return Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2d_ctx(argon2_context *context);
+
+/**
+ * Argon2i: Version of Argon2 that picks memory blocks
+ * independent on the password and salt. Good for side-channels,
+ * but worse w.r.t. tradeoff attacks if only one pass is used.
+ *****
+ * @param context Pointer to current Argon2 context
+ * @return Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2i_ctx(argon2_context *context);
+
+/**
+ * Argon2id: Version of Argon2 where the first half-pass over memory is
+ * password-independent, the rest are password-dependent (on the password and
+ * salt). OK against side channels (they reduce to 1/2-pass Argon2i), and
+ * better with w.r.t. tradeoff attacks (similar to Argon2d).
+ *****
+ * @param context Pointer to current Argon2 context
+ * @return Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2id_ctx(argon2_context *context);
+
+/**
+ * Verify if a given password is correct for Argon2d hashing
+ * @param context Pointer to current Argon2 context
+ * @param hash The password hash to verify. The length of the hash is
+ * specified by the context outlen member
+ * @return Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash);
+
+/**
+ * Verify if a given password is correct for Argon2i hashing
+ * @param context Pointer to current Argon2 context
+ * @param hash The password hash to verify. The length of the hash is
+ * specified by the context outlen member
+ * @return Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash);
+
+/**
+ * Verify if a given password is correct for Argon2id hashing
+ * @param context Pointer to current Argon2 context
+ * @param hash The password hash to verify. The length of the hash is
+ * specified by the context outlen member
+ * @return Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2id_verify_ctx(argon2_context *context,
+ const char *hash);
+
+/* generic function underlying the above ones */
+ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash,
+ argon2_type type);
+
+/**
+ * Get the associated error message for given error code
+ * @return The error message associated with the given error code
+ */
+ARGON2_PUBLIC const char *argon2_error_message(int error_code);
+
+/**
+ * Returns the encoded hash length for the given input parameters
+ * @param t_cost Number of iterations
+ * @param m_cost Memory usage in kibibytes
+ * @param parallelism Number of threads; used to compute lanes
+ * @param saltlen Salt size in bytes
+ * @param hashlen Hash size in bytes
+ * @param type The argon2_type that we want the encoded length for
+ * @return The encoded hash length in bytes
+ */
+ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost,
+ uint32_t parallelism, uint32_t saltlen,
+ uint32_t hashlen, argon2_type type);
+
+/* signals availability of argon2_select_impl: */
+#define ARGON2_SELECTABLE_IMPL
+
+/**
+ * Selects the fastest available optimized implementation.
+ * @param out The file for debug output (e. g. stderr; pass NULL for no
+ * debug output)
+ * @param prefix What to print before each line; NULL is equivalent to empty
+ * string
+ */
+ARGON2_PUBLIC void argon2_select_impl(FILE *out, const char *prefix);
+
+/* signals support for passing preallocated memory: */
+#define ARGON2_PREALLOCATED_MEMORY
+
+ARGON2_PUBLIC size_t argon2_memory_size(uint32_t m_cost, uint32_t parallelism);
+
+/**
+ * Function that performs memory-hard hashing with certain degree of parallelism
+ * @param context Pointer to the Argon2 internal structure
+ * @param type The Argon2 type
+ * @param memory Preallocated memory for blocks (or NULL)
+ * @param memory_size The size of preallocated memory
+ * @return Error code if smth is wrong, ARGON2_OK otherwise
+ */
+ARGON2_PUBLIC int argon2_ctx_mem(argon2_context *context, argon2_type type,
+ void *memory, size_t memory_size);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/argtable3.c b/src/argtable3.c
new file mode 100644
index 0000000..b96ddae
--- /dev/null
+++ b/src/argtable3.c
@@ -0,0 +1,5018 @@
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include "argtable3.h"
+
+// On Windows isspace crashes app in case of using Unicode character set and string to be above ASCII
+// so you have to use _istspace instead of space
+#ifdef UNICODE
+#include <tchar.h>
+ #define ISSPACE _istspace
+#else
+ #define ISSPACE isspace
+#endif
+
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 2013 Tom G. Huang
+ * <tomghuang@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#ifndef ARG_UTILS_H
+#define ARG_UTILS_H
+
+#define ARG_ENABLE_TRACE 0
+#define ARG_ENABLE_LOG 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum
+{
+ EMINCOUNT = 1,
+ EMAXCOUNT,
+ EBADINT,
+ // The same name define EOVERFLOW in errno.h on windows platform
+#ifdef __STDC_WANT_SECURE_LIB__
+ EOVERFLOW_,
+#else
+ EOVERFLOW,
+#endif
+ EBADDOUBLE,
+ EBADDATE,
+ EREGNOMATCH
+};
+
+
+#if defined(_MSC_VER)
+#define ARG_TRACE(x) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) \
+ __pragma(warning(pop))
+
+#define ARG_LOG(x) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) \
+ __pragma(warning(pop))
+#else
+#define ARG_TRACE(x) \
+ do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0)
+
+#define ARG_LOG(x) \
+ do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0)
+#endif
+
+extern void dbg_printf(const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdarg.h>
+#include <stdio.h>
+
+
+void dbg_printf(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+
+/* $Id: getopt.h,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */
+/* $OpenBSD: getopt.h,v 1.1 2002/12/03 20:24:29 millert Exp $ */
+/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+#if 0
+#include <sys/cdefs.h>
+#endif
+
+/*
+ * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
+ */
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+struct option {
+ /* name of long option */
+ const char *name;
+ /*
+ * one of no_argument, required_argument, and optional_argument:
+ * whether option takes an argument
+ */
+ int has_arg;
+ /* if not NULL, set *flag to val when option found */
+ int *flag;
+ /* if flag not NULL, value to set *flag to; else return value */
+ int val;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int getopt_long(int, char * const *, const char *,
+ const struct option *, int *);
+int getopt_long_only(int, char * const *, const char *,
+ const struct option *, int *);
+#ifndef _GETOPT_DEFINED
+#define _GETOPT_DEFINED
+int getopt(int, char * const *, const char *);
+int getsubopt(char **, char * const *, char **);
+
+extern char *optarg; /* getopt(3) external variables */
+extern int opterr;
+extern int optind;
+extern int optopt;
+extern int optreset;
+extern char *suboptarg; /* getsubopt(3) external variable */
+#endif /* _GETOPT_DEFINED */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !_GETOPT_H_ */
+/* $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */
+/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+// $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $"
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 0
+#include <err.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
+
+#ifdef REPLACE_GETOPT
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+
+
+#ifdef _WIN32
+
+/* Windows needs warnx(). We change the definition though:
+ * 1. (another) global is defined, opterrmsg, which holds the error message
+ * 2. errors are always printed out on stderr w/o the program name
+ * Note that opterrmsg always gets set no matter what opterr is set to. The
+ * error message will not be printed if opterr is 0 as usual.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#define MAX_OPTER_MSG_SIZE 128
+
+extern char opterrmsg[MAX_OPTER_MSG_SIZE];
+char opterrmsg[MAX_OPTER_MSG_SIZE]; /* buffer for the last error message */
+
+static void warnx(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ /*
+ Make sure opterrmsg is always zero-terminated despite the _vsnprintf()
+ implementation specifics and manually suppress the warning.
+ */
+ memset(opterrmsg, 0, sizeof opterrmsg);
+ if (fmt != NULL)
+#ifdef __STDC_WANT_SECURE_LIB__
+ _vsnprintf_s(opterrmsg, MAX_OPTER_MSG_SIZE, sizeof(opterrmsg) - 1, fmt, ap);
+#else
+ _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap);
+#endif
+ va_end(ap);
+
+//#pragma warning(suppress: 6053)
+ fprintf(stderr, "%s\n", opterrmsg);
+}
+
+#else
+#include <err.h>
+#endif /*_WIN32*/
+
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int short_too)
+{
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = place;
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int flags)
+{
+ char *oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+#ifdef __STDC_WANT_SECURE_LIB__
+ char* buffer = NULL;
+ size_t buffer_size = 0;
+ errno_t err = 0;
+#endif
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+
+#ifdef __STDC_WANT_SECURE_LIB__
+ if (posixly_correct == -1) {
+ err = _dupenv_s(&buffer, &buffer_size, "POSIXLY_CORRECT") == 0;
+ posixly_correct = buffer != NULL;
+ if(buffer != NULL && err == 0) {
+ free(buffer);
+ }
+ }
+#else
+ if (posixly_correct == -1)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+#endif
+ if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ else if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif /* REPLACE_GETOPT */
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "argtable3.h"
+
+
+char * arg_strptime(const char *buf, const char *fmt, struct tm *tm);
+
+
+static void arg_date_resetfn(struct arg_date *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_date_scanfn(struct arg_date *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* no argument value was given, leave parent->tmval[] unaltered but still count it */
+ parent->count++;
+ }
+ else
+ {
+ const char *pend;
+ struct tm tm = parent->tmval[parent->count];
+
+ /* parse the given argument value, store result in parent->tmval[] */
+ pend = arg_strptime(argval, parent->format, &tm);
+ if (pend && pend[0] == '\0')
+ parent->tmval[parent->count++] = tm;
+ else
+ errorcode = EBADDATE;
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_date_checkfn(struct arg_date *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_date_errorfn(
+ struct arg_date *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EBADDATE:
+ {
+ struct tm tm;
+ char buff[200];
+
+ fprintf(fp, "illegal timestamp format \"%s\"\n", argval);
+ memset(&tm, 0, sizeof(tm));
+ arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm);
+ strftime(buff, sizeof(buff), parent->format, &tm);
+ printf("correct format is \"%s\"\n", buff);
+ break;
+ }
+ }
+}
+
+
+struct arg_date * arg_date0(
+ const char * shortopts,
+ const char * longopts,
+ const char * format,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary);
+}
+
+
+struct arg_date * arg_date1(
+ const char * shortopts,
+ const char * longopts,
+ const char * format,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary);
+}
+
+
+struct arg_date * arg_daten(
+ const char * shortopts,
+ const char * longopts,
+ const char * format,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_date *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ /* default time format is the national date format for the locale */
+ if (!format)
+ format = "%x";
+
+ nbytes = sizeof(struct arg_date) /* storage for struct arg_date */
+ + maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */
+
+ /* allocate storage for the arg_date struct + tmval[] array. */
+ /* we use calloc because we want the tmval[] array zero filled. */
+ result = (struct arg_date *)calloc(1, nbytes);
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : format;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_date_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_date_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_date_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_date_errorfn;
+
+ /* store the tmval[maxcount] array immediately after the arg_date struct */
+ result->tmval = (struct tm *)(result + 1);
+
+ /* init the remaining arg_date member variables */
+ result->count = 0;
+ result->format = format;
+ }
+
+ ARG_TRACE(("arg_daten() returns %p\n", result));
+ return result;
+}
+
+
+/*-
+ * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Klaus Klein.
+ * Heavily optimised by David Laight
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+
+/*
+ * We do not implement alternate representations. However, we always
+ * check whether a given modifier is allowed for a certain conversion.
+ */
+#define ALT_E 0x01
+#define ALT_O 0x02
+#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
+#define TM_YEAR_BASE (1900)
+
+static int conv_num(const char * *, int *, int, int);
+
+static const char *day[7] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday"
+};
+
+static const char *abday[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static const char *mon[12] = {
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December"
+};
+
+static const char *abmon[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const char *am_pm[2] = {
+ "AM", "PM"
+};
+
+
+static int arg_strcasecmp(const char *s1, const char *s2)
+{
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+ while (tolower(*us1) == tolower(*us2++))
+ if (*us1++ == '\0')
+ return 0;
+
+ return tolower(*us1) - tolower(*--us2);
+}
+
+
+static int arg_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ if (n != 0)
+ {
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+ do
+ {
+ if (tolower(*us1) != tolower(*us2++))
+ return tolower(*us1) - tolower(*--us2);
+
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+
+ return 0;
+}
+
+
+char * arg_strptime(const char *buf, const char *fmt, struct tm *tm)
+{
+ char c;
+ const char *bp;
+ size_t len = 0;
+ int alt_format, i, split_year = 0;
+
+ bp = buf;
+
+ while ((c = *fmt) != '\0') {
+ /* Clear `alternate' modifier prior to new conversion. */
+ alt_format = 0;
+
+ /* Eat up white-space. */
+ if (ISSPACE(c)) {
+ while (ISSPACE(*bp))
+ bp++;
+
+ fmt++;
+ continue;
+ }
+
+ if ((c = *fmt++) != '%')
+ goto literal;
+
+
+again:
+ switch (c = *fmt++)
+ {
+ case '%': /* "%%" is converted to "%". */
+literal:
+ if (c != *bp++)
+ return (0);
+ break;
+
+ /*
+ * "Alternative" modifiers. Just set the appropriate flag
+ * and start over again.
+ */
+ case 'E': /* "%E?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_E;
+ goto again;
+
+ case 'O': /* "%O?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_O;
+ goto again;
+
+ /*
+ * "Complex" conversion rules, implemented through recursion.
+ */
+ case 'c': /* Date and time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ bp = arg_strptime(bp, "%x %X", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'D': /* The date as "%m/%d/%y". */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%m/%d/%y", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'R': /* The time as "%H:%M". */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%H:%M", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'r': /* The time in 12-hour clock representation. */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%I:%M:%S %p", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'T': /* The time as "%H:%M:%S". */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%H:%M:%S", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'X': /* The time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ bp = arg_strptime(bp, "%H:%M:%S", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'x': /* The date, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ bp = arg_strptime(bp, "%m/%d/%y", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ /*
+ * "Elementary" conversion rules.
+ */
+ case 'A': /* The day of week, using the locale's form. */
+ case 'a':
+ LEGAL_ALT(0);
+ for (i = 0; i < 7; i++) {
+ /* Full name. */
+ len = strlen(day[i]);
+ if (arg_strncasecmp(day[i], bp, len) == 0)
+ break;
+
+ /* Abbreviated name. */
+ len = strlen(abday[i]);
+ if (arg_strncasecmp(abday[i], bp, len) == 0)
+ break;
+ }
+
+ /* Nothing matched. */
+ if (i == 7)
+ return (0);
+
+ tm->tm_wday = i;
+ bp += len;
+ break;
+
+ case 'B': /* The month, using the locale's form. */
+ case 'b':
+ case 'h':
+ LEGAL_ALT(0);
+ for (i = 0; i < 12; i++) {
+ /* Full name. */
+ len = strlen(mon[i]);
+ if (arg_strncasecmp(mon[i], bp, len) == 0)
+ break;
+
+ /* Abbreviated name. */
+ len = strlen(abmon[i]);
+ if (arg_strncasecmp(abmon[i], bp, len) == 0)
+ break;
+ }
+
+ /* Nothing matched. */
+ if (i == 12)
+ return (0);
+
+ tm->tm_mon = i;
+ bp += len;
+ break;
+
+ case 'C': /* The century number. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 99)))
+ return (0);
+
+ if (split_year) {
+ tm->tm_year = (tm->tm_year % 100) + (i * 100);
+ } else {
+ tm->tm_year = i * 100;
+ split_year = 1;
+ }
+ break;
+
+ case 'd': /* The day of month. */
+ case 'e':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
+ return (0);
+ break;
+
+ case 'k': /* The hour (24-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'H':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
+ return (0);
+ break;
+
+ case 'l': /* The hour (12-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'I':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
+ return (0);
+ if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ break;
+
+ case 'j': /* The day of year. */
+ LEGAL_ALT(0);
+ if (!(conv_num(&bp, &i, 1, 366)))
+ return (0);
+ tm->tm_yday = i - 1;
+ break;
+
+ case 'M': /* The minute. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
+ return (0);
+ break;
+
+ case 'm': /* The month. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &i, 1, 12)))
+ return (0);
+ tm->tm_mon = i - 1;
+ break;
+
+ case 'p': /* The locale's equivalent of AM/PM. */
+ LEGAL_ALT(0);
+ /* AM? */
+ if (arg_strcasecmp(am_pm[0], bp) == 0) {
+ if (tm->tm_hour > 11)
+ return (0);
+
+ bp += strlen(am_pm[0]);
+ break;
+ }
+ /* PM? */
+ else if (arg_strcasecmp(am_pm[1], bp) == 0) {
+ if (tm->tm_hour > 11)
+ return (0);
+
+ tm->tm_hour += 12;
+ bp += strlen(am_pm[1]);
+ break;
+ }
+
+ /* Nothing matched. */
+ return (0);
+
+ case 'S': /* The seconds. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
+ return (0);
+ break;
+
+ case 'U': /* The week of year, beginning on sunday. */
+ case 'W': /* The week of year, beginning on monday. */
+ LEGAL_ALT(ALT_O);
+ /*
+ * XXX This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so just check the
+ * range for now.
+ */
+ if (!(conv_num(&bp, &i, 0, 53)))
+ return (0);
+ break;
+
+ case 'w': /* The day of week, beginning on sunday. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
+ return (0);
+ break;
+
+ case 'Y': /* The year. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 9999)))
+ return (0);
+
+ tm->tm_year = i - TM_YEAR_BASE;
+ break;
+
+ case 'y': /* The year within 100 years of the epoch. */
+ LEGAL_ALT(ALT_E | ALT_O);
+ if (!(conv_num(&bp, &i, 0, 99)))
+ return (0);
+
+ if (split_year) {
+ tm->tm_year = ((tm->tm_year / 100) * 100) + i;
+ break;
+ }
+ split_year = 1;
+ if (i <= 68)
+ tm->tm_year = i + 2000 - TM_YEAR_BASE;
+ else
+ tm->tm_year = i + 1900 - TM_YEAR_BASE;
+ break;
+
+ /*
+ * Miscellaneous conversions.
+ */
+ case 'n': /* Any kind of white-space. */
+ case 't':
+ LEGAL_ALT(0);
+ while (ISSPACE(*bp))
+ bp++;
+ break;
+
+
+ default: /* Unknown/unsupported conversion. */
+ return (0);
+ }
+
+
+ }
+
+ /* LINTED functional specification */
+ return ((char *)bp);
+}
+
+
+static int conv_num(const char * *buf, int *dest, int llim, int ulim)
+{
+ int result = 0;
+
+ /* The limit also determines the number of valid digits. */
+ int rulim = ulim;
+
+ if (**buf < '0' || **buf > '9')
+ return (0);
+
+ do {
+ result *= 10;
+ result += *(*buf)++ - '0';
+ rulim /= 10;
+ } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
+
+ if (result < llim || result > ulim)
+ return (0);
+
+ *dest = result;
+ return (1);
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+
+#include "argtable3.h"
+
+
+static void arg_dbl_resetfn(struct arg_dbl *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_dbl_scanfn(struct arg_dbl *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent argument value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ double val;
+ char *end;
+
+ /* extract double from argval into val */
+ val = strtod(argval, &end);
+
+ /* if success then store result in parent->dval[] array otherwise return error*/
+ if (*end == 0)
+ parent->dval[parent->count++] = val;
+ else
+ errorcode = EBADDOUBLE;
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_dbl_checkfn(struct arg_dbl *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_dbl_errorfn(
+ struct arg_dbl *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EBADDOUBLE:
+ fprintf(fp, "invalid argument \"%s\" to option ", argval);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+ }
+}
+
+
+struct arg_dbl * arg_dbl0(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_dbl * arg_dbl1(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_dbl * arg_dbln(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_dbl *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */
+ + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */
+
+ result = (struct arg_dbl *)malloc(nbytes);
+ if (result)
+ {
+ size_t addr;
+ size_t rem;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : "<double>";
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_dbl_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_dbl_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_dbl_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_dbl_errorfn;
+
+ /* Store the dval[maxcount] array on the first double boundary that
+ * immediately follows the arg_dbl struct. We do the memory alignment
+ * purely for SPARC and Motorola systems. They require floats and
+ * doubles to be aligned on natural boundaries.
+ */
+ addr = (size_t)(result + 1);
+ rem = addr % sizeof(double);
+ result->dval = (double *)(addr + sizeof(double) - rem);
+ ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem));
+
+ result->count = 0;
+ }
+
+ ARG_TRACE(("arg_dbln() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+
+#include "argtable3.h"
+
+
+static void arg_end_resetfn(struct arg_end *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+static void arg_end_errorfn(
+ void *parent,
+ FILE *fp,
+ int error,
+ const char *argval,
+ const char *progname)
+{
+ /* suppress unreferenced formal parameter warning */
+ (void)parent;
+
+ progname = progname ? progname : "";
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(error)
+ {
+ case ARG_ELIMIT:
+ fputs("too many errors to display", fp);
+ break;
+ case ARG_EMALLOC:
+ fputs("insufficent memory", fp);
+ break;
+ case ARG_ENOMATCH:
+ fprintf(fp, "unexpected argument \"%s\"", argval);
+ break;
+ case ARG_EMISSARG:
+ fprintf(fp, "option \"%s\" requires an argument", argval);
+ break;
+ case ARG_ELONGOPT:
+ fprintf(fp, "invalid option \"%s\"", argval);
+ break;
+ default:
+ fprintf(fp, "invalid option \"-%c\"", error);
+ break;
+ }
+
+ fputc('\n', fp);
+}
+
+
+struct arg_end * arg_end(int maxcount)
+{
+ size_t nbytes;
+ struct arg_end *result;
+
+ nbytes = sizeof(struct arg_end)
+ + maxcount * sizeof(int) /* storage for int error[maxcount] array*/
+ + maxcount * sizeof(void *) /* storage for void* parent[maxcount] array */
+ + maxcount * sizeof(char *); /* storage for char* argval[maxcount] array */
+
+ result = (struct arg_end *)malloc(nbytes);
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_TERMINATOR;
+ result->hdr.shortopts = NULL;
+ result->hdr.longopts = NULL;
+ result->hdr.datatype = NULL;
+ result->hdr.glossary = NULL;
+ result->hdr.mincount = 1;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_end_resetfn;
+ result->hdr.scanfn = NULL;
+ result->hdr.checkfn = NULL;
+ result->hdr.errorfn = (arg_errorfn *)arg_end_errorfn;
+
+ /* store error[maxcount] array immediately after struct arg_end */
+ result->error = (int *)(result + 1);
+
+ /* store parent[maxcount] array immediately after error[] array */
+ result->parent = (void * *)(result->error + maxcount );
+
+ /* store argval[maxcount] array immediately after parent[] array */
+ result->argval = (const char * *)(result->parent + maxcount );
+ }
+
+ ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result));
+ return result;
+}
+
+
+void arg_print_errors(FILE * fp, struct arg_end * end, const char * progname)
+{
+ int i;
+ ARG_TRACE(("arg_errors()\n"));
+ for (i = 0; i < end->count; i++)
+ {
+ struct arg_hdr *errorparent = (struct arg_hdr *)(end->parent[i]);
+ if (errorparent->errorfn)
+ errorparent->errorfn(end->parent[i],
+ fp,
+ end->error[i],
+ end->argval[i],
+ progname);
+ }
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "argtable3.h"
+
+#ifdef WIN32
+# define FILESEPARATOR1 '\\'
+# define FILESEPARATOR2 '/'
+#else
+# define FILESEPARATOR1 '/'
+# define FILESEPARATOR2 '/'
+#endif
+
+
+static void arg_file_resetfn(struct arg_file *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+/* Returns ptr to the base filename within *filename */
+static const char * arg_basename(const char *filename)
+{
+ const char *result = NULL, *result1, *result2;
+
+ /* Find the last occurrence of eother file separator character. */
+ /* Two alternative file separator chars are supported as legal */
+ /* file separators but not both together in the same filename. */
+ result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL);
+ result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL);
+
+ if (result2)
+ result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */
+
+ if (result1)
+ result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */
+
+ if (!result)
+ result = filename; /* neither file separator was found so basename is the whole filename */
+
+ /* special cases of "." and ".." are not considered basenames */
+ if (result && ( strcmp(".", result) == 0 || strcmp("..", result) == 0 ))
+ result = filename + strlen(filename);
+
+ return result;
+}
+
+
+/* Returns ptr to the file extension within *basename */
+static const char * arg_extension(const char *basename)
+{
+ /* find the last occurrence of '.' in basename */
+ const char *result = (basename ? strrchr(basename, '.') : NULL);
+
+ /* if no '.' was found then return pointer to end of basename */
+ if (basename && !result)
+ result = basename + strlen(basename);
+
+ /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */
+ if (basename && result == basename)
+ result = basename + strlen(basename);
+
+ /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */
+ if (basename && result && result[1] == '\0')
+ result = basename + strlen(basename);
+
+ return result;
+}
+
+
+static int arg_file_scanfn(struct arg_file *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent arguiment value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ parent->filename[parent->count] = argval;
+ parent->basename[parent->count] = arg_basename(argval);
+ parent->extension[parent->count] =
+ arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/
+ parent->count++;
+ }
+
+ ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_file_checkfn(struct arg_file *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_file_errorfn(
+ struct arg_file *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ default:
+ fprintf(fp, "unknown error at \"%s\"\n", argval);
+ }
+}
+
+
+struct arg_file * arg_file0(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_filen(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_file * arg_file1(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_filen(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_file * arg_filen(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_file *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_file) /* storage for struct arg_file */
+ + sizeof(char *) * maxcount /* storage for filename[maxcount] array */
+ + sizeof(char *) * maxcount /* storage for basename[maxcount] array */
+ + sizeof(char *) * maxcount; /* storage for extension[maxcount] array */
+
+ result = (struct arg_file *)malloc(nbytes);
+ if (result)
+ {
+ int i;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.glossary = glossary;
+ result->hdr.datatype = datatype ? datatype : "<file>";
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_file_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_file_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_file_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_file_errorfn;
+
+ /* store the filename,basename,extension arrays immediately after the arg_file struct */
+ result->filename = (const char * *)(result + 1);
+ result->basename = result->filename + maxcount;
+ result->extension = result->basename + maxcount;
+ result->count = 0;
+
+ /* foolproof the string pointers by initialising them with empty strings */
+ for (i = 0; i < maxcount; i++)
+ {
+ result->filename[i] = "";
+ result->basename[i] = "";
+ result->extension[i] = "";
+ }
+ }
+
+ ARG_TRACE(("arg_filen() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "argtable3.h"
+
+
+static void arg_int_resetfn(struct arg_int *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+/* strtol0x() is like strtol() except that the numeric string is */
+/* expected to be prefixed by "0X" where X is a user supplied char. */
+/* The string may optionally be prefixed by white space and + or - */
+/* as in +0X123 or -0X123. */
+/* Once the prefix has been scanned, the remainder of the numeric */
+/* string is converted using strtol() with the given base. */
+/* eg: to parse hex str="-0X12324", specify X='X' and base=16. */
+/* eg: to parse oct str="+0o12324", specify X='O' and base=8. */
+/* eg: to parse bin str="-0B01010", specify X='B' and base=2. */
+/* Failure of conversion is indicated by result where *endptr==str. */
+static long int strtol0X(const char * str,
+ const char * *endptr,
+ char X,
+ int base)
+{
+ long int val; /* stores result */
+ int s = 1; /* sign is +1 or -1 */
+ const char *ptr = str; /* ptr to current position in str */
+
+ /* skip leading whitespace */
+ while (ISSPACE(*ptr))
+ ptr++;
+ /* printf("1) %s\n",ptr); */
+
+ /* scan optional sign character */
+ switch (*ptr)
+ {
+ case '+':
+ ptr++;
+ s = 1;
+ break;
+ case '-':
+ ptr++;
+ s = -1;
+ break;
+ default:
+ s = 1;
+ break;
+ }
+ /* printf("2) %s\n",ptr); */
+
+ /* '0X' prefix */
+ if ((*ptr++) != '0')
+ {
+ /* printf("failed to detect '0'\n"); */
+ *endptr = str;
+ return 0;
+ }
+ /* printf("3) %s\n",ptr); */
+ if (toupper(*ptr++) != toupper(X))
+ {
+ /* printf("failed to detect '%c'\n",X); */
+ *endptr = str;
+ return 0;
+ }
+ /* printf("4) %s\n",ptr); */
+
+ /* attempt conversion on remainder of string using strtol() */
+ val = strtol(ptr, (char * *)endptr, base);
+ if (*endptr == ptr)
+ {
+ /* conversion failed */
+ *endptr = str;
+ return 0;
+ }
+
+ /* success */
+ return s * val;
+}
+
+
+/* Returns 1 if str matches suffix (case insensitive). */
+/* Str may contain trailing whitespace, but nothing else. */
+static int detectsuffix(const char *str, const char *suffix)
+{
+ /* scan pairwise through strings until mismatch detected */
+ while( toupper(*str) == toupper(*suffix) )
+ {
+ /* printf("'%c' '%c'\n", *str, *suffix); */
+
+ /* return 1 (success) if match persists until the string terminator */
+ if (*str == '\0')
+ return 1;
+
+ /* next chars */
+ str++;
+ suffix++;
+ }
+ /* printf("'%c' '%c' mismatch\n", *str, *suffix); */
+
+ /* return 0 (fail) if the matching did not consume the entire suffix */
+ if (*suffix != 0)
+ return 0; /* failed to consume entire suffix */
+
+ /* skip any remaining whitespace in str */
+ while (ISSPACE(*str))
+ str++;
+
+ /* return 1 (success) if we have reached end of str else return 0 (fail) */
+ return (*str == '\0') ? 1 : 0;
+}
+
+
+static int arg_int_scanfn(struct arg_int *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent arguiment value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ long int val;
+ const char *end;
+
+ /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */
+ val = strtol0X(argval, &end, 'X', 16);
+ if (end == argval)
+ {
+ /* hex failed, attempt octal conversion (eg +0o123) */
+ val = strtol0X(argval, &end, 'O', 8);
+ if (end == argval)
+ {
+ /* octal failed, attempt binary conversion (eg +0B101) */
+ val = strtol0X(argval, &end, 'B', 2);
+ if (end == argval)
+ {
+ /* binary failed, attempt decimal conversion with no prefix (eg 1234) */
+ val = strtol(argval, (char * *)&end, 10);
+ if (end == argval)
+ {
+ /* all supported number formats failed */
+ return EBADINT;
+ }
+ }
+ }
+ }
+
+ /* Safety check for integer overflow. WARNING: this check */
+ /* achieves nothing on machines where size(int)==size(long). */
+ if ( val > INT_MAX || val < INT_MIN )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_;
+#else
+ errorcode = EOVERFLOW;
+#endif
+
+ /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */
+ /* We need to be mindful of integer overflows when using such big numbers. */
+ if (detectsuffix(end, "KB")) /* kilobytes */
+ {
+ if ( val > (INT_MAX / 1024) || val < (INT_MIN / 1024) )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */
+#else
+ errorcode = EOVERFLOW; /* Overflow would occur if we proceed */
+#endif
+ else
+ val *= 1024; /* 1KB = 1024 */
+ }
+ else if (detectsuffix(end, "MB")) /* megabytes */
+ {
+ if ( val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576) )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */
+#else
+ errorcode = EOVERFLOW; /* Overflow would occur if we proceed */
+#endif
+ else
+ val *= 1048576; /* 1MB = 1024*1024 */
+ }
+ else if (detectsuffix(end, "GB")) /* gigabytes */
+ {
+ if ( val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824) )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */
+#else
+ errorcode = EOVERFLOW; /* Overflow would occur if we proceed */
+#endif
+ else
+ val *= 1073741824; /* 1GB = 1024*1024*1024 */
+ }
+ else if (!detectsuffix(end, ""))
+ errorcode = EBADINT; /* invalid suffix detected */
+
+ /* if success then store result in parent->ival[] array */
+ if (errorcode == 0)
+ parent->ival[parent->count++] = val;
+ }
+
+ /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */
+ return errorcode;
+}
+
+
+static int arg_int_checkfn(struct arg_int *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+ /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
+ return errorcode;
+}
+
+
+static void arg_int_errorfn(
+ struct arg_int *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EBADINT:
+ fprintf(fp, "invalid argument \"%s\" to option ", argval);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+#ifdef __STDC_WANT_SECURE_LIB__
+ case EOVERFLOW_:
+#else
+ case EOVERFLOW:
+#endif
+ fputs("integer overflow at option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, " ");
+ fprintf(fp, "(%s is too large)\n", argval);
+ break;
+ }
+}
+
+
+struct arg_int * arg_int0(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_intn(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_int * arg_int1(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_intn(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_int * arg_intn(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_int *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_int) /* storage for struct arg_int */
+ + maxcount * sizeof(int); /* storage for ival[maxcount] array */
+
+ result = (struct arg_int *)malloc(nbytes);
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : "<int>";
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_int_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_int_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_int_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_int_errorfn;
+
+ /* store the ival[maxcount] array immediately after the arg_int struct */
+ result->ival = (int *)(result + 1);
+ result->count = 0;
+ }
+
+ ARG_TRACE(("arg_intn() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+
+#include "argtable3.h"
+
+
+static void arg_lit_resetfn(struct arg_lit *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_lit_scanfn(struct arg_lit *parent, const char *argval)
+{
+ int errorcode = 0;
+ if (parent->count < parent->hdr.maxcount )
+ parent->count++;
+ else
+ errorcode = EMAXCOUNT;
+
+ ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval,
+ errorcode));
+ return errorcode;
+}
+
+
+static int arg_lit_checkfn(struct arg_lit *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_lit_errorfn(
+ struct arg_lit *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fprintf(fp, "%s: missing option ", progname);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ fprintf(fp, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fprintf(fp, "%s: extraneous option ", progname);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+ }
+
+ ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp,
+ errorcode, argval, progname));
+}
+
+
+struct arg_lit * arg_lit0(
+ const char * shortopts,
+ const char * longopts,
+ const char * glossary)
+{
+ return arg_litn(shortopts, longopts, 0, 1, glossary);
+}
+
+
+struct arg_lit * arg_lit1(
+ const char *shortopts,
+ const char *longopts,
+ const char *glossary)
+{
+ return arg_litn(shortopts, longopts, 1, 1, glossary);
+}
+
+
+struct arg_lit * arg_litn(
+ const char *shortopts,
+ const char *longopts,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ struct arg_lit *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ result = (struct arg_lit *)malloc(sizeof(struct arg_lit));
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = 0;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = NULL;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_lit_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_lit_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_lit_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_lit_errorfn;
+
+ /* init local variables */
+ result->count = 0;
+ }
+
+ ARG_TRACE(("arg_litn() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+
+#include "argtable3.h"
+
+struct arg_rem *arg_rem(const char *datatype, const char *glossary)
+{
+ struct arg_rem *result = (struct arg_rem *)malloc(sizeof(struct arg_rem));
+ if (result)
+ {
+ result->hdr.flag = 0;
+ result->hdr.shortopts = NULL;
+ result->hdr.longopts = NULL;
+ result->hdr.datatype = datatype;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = 1;
+ result->hdr.maxcount = 1;
+ result->hdr.parent = result;
+ result->hdr.resetfn = NULL;
+ result->hdr.scanfn = NULL;
+ result->hdr.checkfn = NULL;
+ result->hdr.errorfn = NULL;
+ }
+
+ ARG_TRACE(("arg_rem() returns %p\n", result));
+ return result;
+}
+
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "argtable3.h"
+
+
+#ifndef _TREX_H_
+#define _TREX_H_
+/***************************************************************
+ T-Rex a tiny regular expression library
+
+ Copyright (C) 2003-2006 Alberto Demichelis
+
+ This software is provided 'as-is', without any express
+ or implied warranty. In no event will the authors be held
+ liable for any damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for
+ any purpose, including commercial applications, and to alter
+ it and redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented;
+ you must not claim that you wrote the original software.
+ If you use this software in a product, an acknowledgment
+ in the product documentation would be appreciated but
+ is not required.
+
+ 2. Altered source versions must be plainly marked as such,
+ and must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any
+ source distribution.
+
+****************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _UNICODE
+#define TRexChar unsigned short
+#define MAX_CHAR 0xFFFF
+#define _TREXC(c) L##c
+#define trex_strlen wcslen
+#define trex_printf wprintf
+#else
+#define TRexChar char
+#define MAX_CHAR 0xFF
+#define _TREXC(c) (c)
+#define trex_strlen strlen
+#define trex_printf printf
+#endif
+
+#ifndef TREX_API
+#define TREX_API extern
+#endif
+
+#define TRex_True 1
+#define TRex_False 0
+
+#define TREX_ICASE ARG_REX_ICASE
+
+typedef unsigned int TRexBool;
+typedef struct TRex TRex;
+
+typedef struct {
+ const TRexChar *begin;
+ int len;
+} TRexMatch;
+
+TREX_API TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags);
+TREX_API void trex_free(TRex *exp);
+TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text);
+TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
+TREX_API TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end);
+TREX_API int trex_getsubexpcount(TRex* exp);
+TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
+
+struct privhdr
+{
+ const char *pattern;
+ int flags;
+};
+
+
+static void arg_rex_resetfn(struct arg_rex *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+static int arg_rex_scanfn(struct arg_rex *parent, const char *argval)
+{
+ int errorcode = 0;
+ const TRexChar *error = NULL;
+ TRex *rex = NULL;
+ TRexBool is_match = TRex_False;
+
+ if (parent->count == parent->hdr.maxcount )
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent argument value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ struct privhdr *priv = (struct privhdr *)parent->hdr.priv;
+
+ /* test the current argument value for a match with the regular expression */
+ /* if a match is detected, record the argument value in the arg_rex struct */
+
+ rex = trex_compile(priv->pattern, &error, priv->flags);
+ is_match = trex_match(rex, argval);
+ if (!is_match)
+ errorcode = EREGNOMATCH;
+ else
+ parent->sval[parent->count++] = argval;
+
+ trex_free(rex);
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode));
+ return errorcode;
+}
+
+static int arg_rex_checkfn(struct arg_rex *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+ //struct privhdr *priv = (struct privhdr*)parent->hdr.priv;
+
+ /* free the regex "program" we constructed in resetfn */
+ //regfree(&(priv->regex));
+
+ /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
+ return errorcode;
+}
+
+static void arg_rex_errorfn(struct arg_rex *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EREGNOMATCH:
+ fputs("illegal value ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ default:
+ {
+ //char errbuff[256];
+ //regerror(errorcode, NULL, errbuff, sizeof(errbuff));
+ //printf("%s\n", errbuff);
+ }
+ break;
+ }
+}
+
+
+struct arg_rex * arg_rex0(const char * shortopts,
+ const char * longopts,
+ const char * pattern,
+ const char *datatype,
+ int flags,
+ const char *glossary)
+{
+ return arg_rexn(shortopts,
+ longopts,
+ pattern,
+ datatype,
+ 0,
+ 1,
+ flags,
+ glossary);
+}
+
+struct arg_rex * arg_rex1(const char * shortopts,
+ const char * longopts,
+ const char * pattern,
+ const char *datatype,
+ int flags,
+ const char *glossary)
+{
+ return arg_rexn(shortopts,
+ longopts,
+ pattern,
+ datatype,
+ 1,
+ 1,
+ flags,
+ glossary);
+}
+
+
+struct arg_rex * arg_rexn(const char * shortopts,
+ const char * longopts,
+ const char * pattern,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ int flags,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_rex *result;
+ struct privhdr *priv;
+ int i;
+ const TRexChar *error = NULL;
+ TRex *rex = NULL;
+
+ if (!pattern)
+ {
+ printf(
+ "argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n");
+ printf("argtable: Bad argument table.\n");
+ return NULL;
+ }
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_rex) /* storage for struct arg_rex */
+ + sizeof(struct privhdr) /* storage for private arg_rex data */
+ + maxcount * sizeof(char *); /* storage for sval[maxcount] array */
+
+ result = (struct arg_rex *)malloc(nbytes);
+ if (result == NULL)
+ return result;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : pattern;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_rex_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_rex_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_rex_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_rex_errorfn;
+
+ /* store the arg_rex_priv struct immediately after the arg_rex struct */
+ result->hdr.priv = result + 1;
+ priv = (struct privhdr *)(result->hdr.priv);
+ priv->pattern = pattern;
+ priv->flags = flags;
+
+ /* store the sval[maxcount] array immediately after the arg_rex_priv struct */
+ result->sval = (const char * *)(priv + 1);
+ result->count = 0;
+
+ /* foolproof the string pointers by initializing them to reference empty strings */
+ for (i = 0; i < maxcount; i++)
+ result->sval[i] = "";
+
+ /* here we construct and destroy a regex representation of the regular
+ * expression for no other reason than to force any regex errors to be
+ * trapped now rather than later. If we don't, then errors may go undetected
+ * until an argument is actually parsed.
+ */
+
+ rex = trex_compile(priv->pattern, &error, priv->flags);
+ if (rex == NULL)
+ {
+ ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern));
+ ARG_LOG(("argtable: Bad argument table.\n"));
+ }
+
+ trex_free(rex);
+
+ ARG_TRACE(("arg_rexn() returns %p\n", result));
+ return result;
+}
+
+
+
+/* see copyright notice in trex.h */
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <setjmp.h>
+
+#ifdef _UINCODE
+#define scisprint iswprint
+#define scstrlen wcslen
+#define scprintf wprintf
+#define _SC(x) L(x)
+#else
+#define scisprint isprint
+#define scstrlen strlen
+#define scprintf printf
+#define _SC(x) (x)
+#endif
+
+#ifdef _DEBUG
+#include <stdio.h>
+
+static const TRexChar *g_nnames[] =
+{
+ _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
+ _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
+ _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
+ _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
+};
+
+#endif
+#define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
+#define OP_OR (MAX_CHAR+2)
+#define OP_EXPR (MAX_CHAR+3) //parentesis ()
+#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
+#define OP_DOT (MAX_CHAR+5)
+#define OP_CLASS (MAX_CHAR+6)
+#define OP_CCLASS (MAX_CHAR+7)
+#define OP_NCLASS (MAX_CHAR+8) //negates class the [^
+#define OP_RANGE (MAX_CHAR+9)
+#define OP_CHAR (MAX_CHAR+10)
+#define OP_EOL (MAX_CHAR+11)
+#define OP_BOL (MAX_CHAR+12)
+#define OP_WB (MAX_CHAR+13)
+
+#define TREX_SYMBOL_ANY_CHAR ('.')
+#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
+#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
+#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
+#define TREX_SYMBOL_BRANCH ('|')
+#define TREX_SYMBOL_END_OF_STRING ('$')
+#define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
+#define TREX_SYMBOL_ESCAPE_CHAR ('\\')
+
+
+typedef int TRexNodeType;
+
+typedef struct tagTRexNode{
+ TRexNodeType type;
+ int left;
+ int right;
+ int next;
+}TRexNode;
+
+struct TRex{
+ const TRexChar *_eol;
+ const TRexChar *_bol;
+ const TRexChar *_p;
+ int _first;
+ int _op;
+ TRexNode *_nodes;
+ int _nallocated;
+ int _nsize;
+ int _nsubexpr;
+ TRexMatch *_matches;
+ int _currsubexp;
+ void *_jmpbuf;
+ const TRexChar **_error;
+ int _flags;
+};
+
+static int trex_list(TRex *exp);
+
+static int trex_newnode(TRex *exp, TRexNodeType type)
+{
+ TRexNode n;
+ int newid;
+ n.type = type;
+ n.next = n.right = n.left = -1;
+ if(type == OP_EXPR)
+ n.right = exp->_nsubexpr++;
+ if(exp->_nallocated < (exp->_nsize + 1)) {
+ exp->_nallocated *= 2;
+ exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
+ }
+ exp->_nodes[exp->_nsize++] = n;
+ newid = exp->_nsize - 1;
+ return (int)newid;
+}
+
+static void trex_error(TRex *exp,const TRexChar *error)
+{
+ if(exp->_error) *exp->_error = error;
+ longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
+}
+
+static void trex_expect(TRex *exp, int n){
+ if((*exp->_p) != n)
+ trex_error(exp, _SC("expected paren"));
+ exp->_p++;
+}
+
+static TRexChar trex_escapechar(TRex *exp)
+{
+ if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
+ exp->_p++;
+ switch(*exp->_p) {
+ case 'v': exp->_p++; return '\v';
+ case 'n': exp->_p++; return '\n';
+ case 't': exp->_p++; return '\t';
+ case 'r': exp->_p++; return '\r';
+ case 'f': exp->_p++; return '\f';
+ default: return (*exp->_p++);
+ }
+ } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
+ return (*exp->_p++);
+}
+
+static int trex_charclass(TRex *exp,int classid)
+{
+ int n = trex_newnode(exp,OP_CCLASS);
+ exp->_nodes[n].left = classid;
+ return n;
+}
+
+static int trex_charnode(TRex *exp,TRexBool isclass)
+{
+ TRexChar t;
+ if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
+ exp->_p++;
+ switch(*exp->_p) {
+ case 'n': exp->_p++; return trex_newnode(exp,'\n');
+ case 't': exp->_p++; return trex_newnode(exp,'\t');
+ case 'r': exp->_p++; return trex_newnode(exp,'\r');
+ case 'f': exp->_p++; return trex_newnode(exp,'\f');
+ case 'v': exp->_p++; return trex_newnode(exp,'\v');
+ case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
+ case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
+ case 'p': case 'P': case 'l': case 'u':
+ {
+ t = *exp->_p; exp->_p++;
+ return trex_charclass(exp,t);
+ }
+ case 'b':
+ case 'B':
+ if(!isclass) {
+ int node = trex_newnode(exp,OP_WB);
+ exp->_nodes[node].left = *exp->_p;
+ exp->_p++;
+ return node;
+ } //else default
+ default:
+ t = *exp->_p; exp->_p++;
+ return trex_newnode(exp,t);
+ }
+ }
+ else if(!scisprint(*exp->_p)) {
+
+ trex_error(exp,_SC("letter expected"));
+ }
+ t = *exp->_p; exp->_p++;
+ return trex_newnode(exp,t);
+}
+static int trex_class(TRex *exp)
+{
+ int ret = -1;
+ int first = -1,chain;
+ if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
+ ret = trex_newnode(exp,OP_NCLASS);
+ exp->_p++;
+ }else ret = trex_newnode(exp,OP_CLASS);
+
+ if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
+ chain = ret;
+ while(*exp->_p != ']' && exp->_p != exp->_eol) {
+ if(*exp->_p == '-' && first != -1){
+ int r,t;
+ if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
+ r = trex_newnode(exp,OP_RANGE);
+ if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
+ if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
+ exp->_nodes[r].left = exp->_nodes[first].type;
+ t = trex_escapechar(exp);
+ exp->_nodes[r].right = t;
+ exp->_nodes[chain].next = r;
+ chain = r;
+ first = -1;
+ }
+ else{
+ if(first!=-1){
+ int c = first;
+ exp->_nodes[chain].next = c;
+ chain = c;
+ first = trex_charnode(exp,TRex_True);
+ }
+ else{
+ first = trex_charnode(exp,TRex_True);
+ }
+ }
+ }
+ if(first!=-1){
+ int c = first;
+ exp->_nodes[chain].next = c;
+ chain = c;
+ first = -1;
+ }
+ /* hack? */
+ exp->_nodes[ret].left = exp->_nodes[ret].next;
+ exp->_nodes[ret].next = -1;
+ return ret;
+}
+
+static int trex_parsenumber(TRex *exp)
+{
+ int ret = *exp->_p-'0';
+ int positions = 10;
+ exp->_p++;
+ while(isdigit(*exp->_p)) {
+ ret = ret*10+(*exp->_p++-'0');
+ if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
+ positions *= 10;
+ };
+ return ret;
+}
+
+static int trex_element(TRex *exp)
+{
+ int ret = -1;
+ switch(*exp->_p)
+ {
+ case '(': {
+ int expr,newn;
+ exp->_p++;
+
+
+ if(*exp->_p =='?') {
+ exp->_p++;
+ trex_expect(exp,':');
+ expr = trex_newnode(exp,OP_NOCAPEXPR);
+ }
+ else
+ expr = trex_newnode(exp,OP_EXPR);
+ newn = trex_list(exp);
+ exp->_nodes[expr].left = newn;
+ ret = expr;
+ trex_expect(exp,')');
+ }
+ break;
+ case '[':
+ exp->_p++;
+ ret = trex_class(exp);
+ trex_expect(exp,']');
+ break;
+ case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
+ case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
+ default:
+ ret = trex_charnode(exp,TRex_False);
+ break;
+ }
+
+ {
+ TRexBool isgreedy = TRex_False;
+ unsigned short p0 = 0, p1 = 0;
+ switch(*exp->_p){
+ case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
+ case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
+ case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
+ case '{':
+ exp->_p++;
+ if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
+ p0 = (unsigned short)trex_parsenumber(exp);
+ /*******************************/
+ switch(*exp->_p) {
+ case '}':
+ p1 = p0; exp->_p++;
+ break;
+ case ',':
+ exp->_p++;
+ p1 = 0xFFFF;
+ if(isdigit(*exp->_p)){
+ p1 = (unsigned short)trex_parsenumber(exp);
+ }
+ trex_expect(exp,'}');
+ break;
+ default:
+ trex_error(exp,_SC(", or } expected"));
+ }
+ /*******************************/
+ isgreedy = TRex_True;
+ break;
+
+ }
+ if(isgreedy) {
+ int nnode = trex_newnode(exp,OP_GREEDY);
+ exp->_nodes[nnode].left = ret;
+ exp->_nodes[nnode].right = ((p0)<<16)|p1;
+ ret = nnode;
+ }
+ }
+ if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
+ int nnode = trex_element(exp);
+ exp->_nodes[ret].next = nnode;
+ }
+
+ return ret;
+}
+
+static int trex_list(TRex *exp)
+{
+ int ret=-1,e;
+ if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
+ exp->_p++;
+ ret = trex_newnode(exp,OP_BOL);
+ }
+ e = trex_element(exp);
+ if(ret != -1) {
+ exp->_nodes[ret].next = e;
+ }
+ else ret = e;
+
+ if(*exp->_p == TREX_SYMBOL_BRANCH) {
+ int temp,tright;
+ exp->_p++;
+ temp = trex_newnode(exp,OP_OR);
+ exp->_nodes[temp].left = ret;
+ tright = trex_list(exp);
+ exp->_nodes[temp].right = tright;
+ ret = temp;
+ }
+ return ret;
+}
+
+static TRexBool trex_matchcclass(int cclass,TRexChar c)
+{
+ switch(cclass) {
+ case 'a': return isalpha(c)?TRex_True:TRex_False;
+ case 'A': return !isalpha(c)?TRex_True:TRex_False;
+ case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
+ case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
+ case 's': return ISSPACE(c)?TRex_True:TRex_False;
+ case 'S': return !ISSPACE(c)?TRex_True:TRex_False;
+ case 'd': return isdigit(c)?TRex_True:TRex_False;
+ case 'D': return !isdigit(c)?TRex_True:TRex_False;
+ case 'x': return isxdigit(c)?TRex_True:TRex_False;
+ case 'X': return !isxdigit(c)?TRex_True:TRex_False;
+ case 'c': return iscntrl(c)?TRex_True:TRex_False;
+ case 'C': return !iscntrl(c)?TRex_True:TRex_False;
+ case 'p': return ispunct(c)?TRex_True:TRex_False;
+ case 'P': return !ispunct(c)?TRex_True:TRex_False;
+ case 'l': return islower(c)?TRex_True:TRex_False;
+ case 'u': return isupper(c)?TRex_True:TRex_False;
+ }
+ return TRex_False; /*cannot happen*/
+}
+
+static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
+{
+ do {
+ switch(node->type) {
+ case OP_RANGE:
+ if (exp->_flags & TREX_ICASE)
+ {
+ if(c >= toupper(node->left) && c <= toupper(node->right)) return TRex_True;
+ if(c >= tolower(node->left) && c <= tolower(node->right)) return TRex_True;
+ }
+ else
+ {
+ if(c >= node->left && c <= node->right) return TRex_True;
+ }
+ break;
+ case OP_CCLASS:
+ if(trex_matchcclass(node->left,c)) return TRex_True;
+ break;
+ default:
+ if (exp->_flags & TREX_ICASE)
+ {
+ if (c == tolower(node->type) || c == toupper(node->type)) return TRex_True;
+ }
+ else
+ {
+ if(c == node->type)return TRex_True;
+ }
+
+ }
+ } while((node->next != -1) && (node = &exp->_nodes[node->next]));
+ return TRex_False;
+}
+
+static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
+{
+
+ TRexNodeType type = node->type;
+ switch(type) {
+ case OP_GREEDY: {
+ //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
+ TRexNode *greedystop = NULL;
+ int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
+ const TRexChar *s=str, *good = str;
+
+ if(node->next != -1) {
+ greedystop = &exp->_nodes[node->next];
+ }
+ else {
+ greedystop = next;
+ }
+
+ while((nmaches == 0xFFFF || nmaches < p1)) {
+
+ const TRexChar *stop;
+ if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
+ break;
+ nmaches++;
+ good=s;
+ if(greedystop) {
+ //checks that 0 matches satisfy the expression(if so skips)
+ //if not would always stop(for instance if is a '?')
+ if(greedystop->type != OP_GREEDY ||
+ (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
+ {
+ TRexNode *gnext = NULL;
+ if(greedystop->next != -1) {
+ gnext = &exp->_nodes[greedystop->next];
+ }else if(next && next->next != -1){
+ gnext = &exp->_nodes[next->next];
+ }
+ stop = trex_matchnode(exp,greedystop,s,gnext);
+ if(stop) {
+ //if satisfied stop it
+ if(p0 == p1 && p0 == nmaches) break;
+ else if(nmaches >= p0 && p1 == 0xFFFF) break;
+ else if(nmaches >= p0 && nmaches <= p1) break;
+ }
+ }
+ }
+
+ if(s >= exp->_eol)
+ break;
+ }
+ if(p0 == p1 && p0 == nmaches) return good;
+ else if(nmaches >= p0 && p1 == 0xFFFF) return good;
+ else if(nmaches >= p0 && nmaches <= p1) return good;
+ return NULL;
+ }
+ case OP_OR: {
+ const TRexChar *asd = str;
+ TRexNode *temp=&exp->_nodes[node->left];
+ while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
+ if(temp->next != -1)
+ temp = &exp->_nodes[temp->next];
+ else
+ return asd;
+ }
+ asd = str;
+ temp = &exp->_nodes[node->right];
+ while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
+ if(temp->next != -1)
+ temp = &exp->_nodes[temp->next];
+ else
+ return asd;
+ }
+ return NULL;
+ break;
+ }
+ case OP_EXPR:
+ case OP_NOCAPEXPR:{
+ TRexNode *n = &exp->_nodes[node->left];
+ const TRexChar *cur = str;
+ int capture = -1;
+ if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
+ capture = exp->_currsubexp;
+ exp->_matches[capture].begin = cur;
+ exp->_currsubexp++;
+ }
+
+ do {
+ TRexNode *subnext = NULL;
+ if(n->next != -1) {
+ subnext = &exp->_nodes[n->next];
+ }else {
+ subnext = next;
+ }
+ if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
+ if(capture != -1){
+ exp->_matches[capture].begin = 0;
+ exp->_matches[capture].len = 0;
+ }
+ return NULL;
+ }
+ } while((n->next != -1) && (n = &exp->_nodes[n->next]));
+
+ if(capture != -1)
+ exp->_matches[capture].len = (int)(cur - exp->_matches[capture].begin);
+ return cur;
+ }
+ case OP_WB:
+ if((str == exp->_bol && !ISSPACE(*str))
+ || ((str == exp->_eol && !ISSPACE(*(str-1))))
+ || ((!ISSPACE(*str) && ISSPACE(*(str+1))))
+ || ((ISSPACE(*str) && !ISSPACE(*(str+1)))) ) {
+ return (node->left == 'b')?str:NULL;
+ }
+ return (node->left == 'b')?NULL:str;
+ case OP_BOL:
+ if(str == exp->_bol) return str;
+ return NULL;
+ case OP_EOL:
+ if(str == exp->_eol) return str;
+ return NULL;
+ case OP_DOT:
+ str++;
+ return str;
+ case OP_NCLASS:
+ case OP_CLASS:
+ if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
+ str++;
+ return str;
+ }
+ return NULL;
+ case OP_CCLASS:
+ if(trex_matchcclass(node->left,*str)) {
+ str++;
+ return str;
+ }
+ return NULL;
+ default: /* char */
+ if (exp->_flags & TREX_ICASE)
+ {
+ if(*str != tolower(node->type) && *str != toupper(node->type)) return NULL;
+ }
+ else
+ {
+ if (*str != node->type) return NULL;
+ }
+ str++;
+ return str;
+ }
+ return NULL;
+}
+
+/* public api */
+TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags)
+{
+ TRex *exp = (TRex *)malloc(sizeof(TRex));
+ exp->_eol = exp->_bol = NULL;
+ exp->_p = pattern;
+ exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
+ exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
+ exp->_nsize = 0;
+ exp->_matches = 0;
+ exp->_nsubexpr = 0;
+ exp->_first = trex_newnode(exp,OP_EXPR);
+ exp->_error = error;
+ exp->_jmpbuf = malloc(sizeof(jmp_buf));
+ exp->_flags = flags;
+ if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
+ int res = trex_list(exp);
+ exp->_nodes[exp->_first].left = res;
+ if(*exp->_p!='\0')
+ trex_error(exp,_SC("unexpected character"));
+#ifdef _DEBUG
+ {
+ int nsize,i;
+ TRexNode *t;
+ nsize = exp->_nsize;
+ t = &exp->_nodes[0];
+ scprintf(_SC("\n"));
+ for(i = 0;i < nsize; i++) {
+ if(exp->_nodes[i].type>MAX_CHAR)
+ scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
+ else
+ scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
+ scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
+ }
+ scprintf(_SC("\n"));
+ }
+#endif
+ exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
+ memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
+ }
+ else{
+ trex_free(exp);
+ return NULL;
+ }
+ return exp;
+}
+
+void trex_free(TRex *exp)
+{
+ if(exp) {
+ if(exp->_nodes) free(exp->_nodes);
+ if(exp->_jmpbuf) free(exp->_jmpbuf);
+ if(exp->_matches) free(exp->_matches);
+ free(exp);
+ }
+}
+
+TRexBool trex_match(TRex* exp,const TRexChar* text)
+{
+ const TRexChar* res = NULL;
+ exp->_bol = text;
+ exp->_eol = text + scstrlen(text);
+ exp->_currsubexp = 0;
+ res = trex_matchnode(exp,exp->_nodes,text,NULL);
+ if(res == NULL || res != exp->_eol)
+ return TRex_False;
+ return TRex_True;
+}
+
+TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
+{
+ const TRexChar *cur = NULL;
+ int node = exp->_first;
+ if(text_begin >= text_end) return TRex_False;
+ exp->_bol = text_begin;
+ exp->_eol = text_end;
+ do {
+ cur = text_begin;
+ while(node != -1) {
+ exp->_currsubexp = 0;
+ cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
+ if(!cur)
+ break;
+ node = exp->_nodes[node].next;
+ }
+ text_begin++;
+ } while(cur == NULL && text_begin != text_end);
+
+ if(cur == NULL)
+ return TRex_False;
+
+ --text_begin;
+
+ if(out_begin) *out_begin = text_begin;
+ if(out_end) *out_end = cur;
+ return TRex_True;
+}
+
+TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
+{
+ return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
+}
+
+int trex_getsubexpcount(TRex* exp)
+{
+ return exp->_nsubexpr;
+}
+
+TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
+{
+ if( n<0 || n >= exp->_nsubexpr) return TRex_False;
+ *subexp = exp->_matches[n];
+ return TRex_True;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+
+#include "argtable3.h"
+
+
+static void arg_str_resetfn(struct arg_str *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_str_scanfn(struct arg_str *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent arguiment value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ parent->sval[parent->count++] = argval;
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_str_checkfn(struct arg_str *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_str_errorfn(
+ struct arg_str *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+ }
+}
+
+
+struct arg_str * arg_str0(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_strn(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_str * arg_str1(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_strn(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_str * arg_strn(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_str *result;
+
+ /* should not allow this stupid error */
+ /* we should return an error code warning this logic error */
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_str) /* storage for struct arg_str */
+ + maxcount * sizeof(char *); /* storage for sval[maxcount] array */
+
+ result = (struct arg_str *)malloc(nbytes);
+ if (result)
+ {
+ int i;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : "<string>";
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_str_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_str_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_str_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_str_errorfn;
+
+ /* store the sval[maxcount] array immediately after the arg_str struct */
+ result->sval = (const char * *)(result + 1);
+ result->count = 0;
+
+ /* foolproof the string pointers by initialising them to reference empty strings */
+ for (i = 0; i < maxcount; i++)
+ result->sval[i] = "";
+ }
+
+ ARG_TRACE(("arg_strn() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "argtable3.h"
+
+static
+void arg_register_error(struct arg_end *end,
+ void *parent,
+ int error,
+ const char *argval)
+{
+ /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */
+ if (end->count < end->hdr.maxcount)
+ {
+ end->error[end->count] = error;
+ end->parent[end->count] = parent;
+ end->argval[end->count] = argval;
+ end->count++;
+ }
+ else
+ {
+ end->error[end->hdr.maxcount - 1] = ARG_ELIMIT;
+ end->parent[end->hdr.maxcount - 1] = end;
+ end->argval[end->hdr.maxcount - 1] = NULL;
+ }
+}
+
+
+/*
+ * Return index of first table entry with a matching short option
+ * or -1 if no match was found.
+ */
+static
+int find_shortoption(struct arg_hdr * *table, char shortopt)
+{
+ int tabindex;
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ if (table[tabindex]->shortopts &&
+ strchr(table[tabindex]->shortopts, shortopt))
+ return tabindex;
+ }
+ return -1;
+}
+
+
+struct longoptions
+{
+ int getoptval;
+ int noptions;
+ struct option *options;
+};
+
+#if 0
+static
+void dump_longoptions(struct longoptions * longoptions)
+{
+ int i;
+ printf("getoptval = %d\n", longoptions->getoptval);
+ printf("noptions = %d\n", longoptions->noptions);
+ for (i = 0; i < longoptions->noptions; i++)
+ {
+ printf("options[%d].name = \"%s\"\n",
+ i,
+ longoptions->options[i].name);
+ printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg);
+ printf("options[%d].flag = %p\n", i, longoptions->options[i].flag);
+ printf("options[%d].val = %d\n", i, longoptions->options[i].val);
+ }
+}
+#endif
+
+static
+struct longoptions * alloc_longoptions(struct arg_hdr * *table)
+{
+ struct longoptions *result;
+ size_t nbytes;
+ int noptions = 1;
+ size_t longoptlen = 0;
+ int tabindex;
+
+ /*
+ * Determine the total number of option structs required
+ * by counting the number of comma separated long options
+ * in all table entries and return the count in noptions.
+ * note: noptions starts at 1 not 0 because we getoptlong
+ * requires a NULL option entry to terminate the option array.
+ * While we are at it, count the number of chars required
+ * to store private copies of all the longoption strings
+ * and return that count in logoptlen.
+ */
+ tabindex = 0;
+ do
+ {
+ const char *longopts = table[tabindex]->longopts;
+ longoptlen += (longopts ? strlen(longopts) : 0) + 1;
+ while (longopts)
+ {
+ noptions++;
+ longopts = strchr(longopts + 1, ',');
+ }
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+ /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/
+
+
+ /* allocate storage for return data structure as: */
+ /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */
+ nbytes = sizeof(struct longoptions)
+ + sizeof(struct option) * noptions
+ + longoptlen;
+ result = (struct longoptions *)malloc(nbytes);
+ if (result)
+ {
+ int option_index = 0;
+ char *store;
+
+ result->getoptval = 0;
+ result->noptions = noptions;
+ result->options = (struct option *)(result + 1);
+ store = (char *)(result->options + noptions);
+
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ const char *longopts = table[tabindex]->longopts;
+
+ while(longopts && *longopts)
+ {
+ char *storestart = store;
+
+ /* copy progressive longopt strings into the store */
+ while (*longopts != 0 && *longopts != ',')
+ *store++ = *longopts++;
+ *store++ = 0;
+ if (*longopts == ',')
+ longopts++;
+ /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/
+
+ result->options[option_index].name = storestart;
+ result->options[option_index].flag = &(result->getoptval);
+ result->options[option_index].val = tabindex;
+ if (table[tabindex]->flag & ARG_HASOPTVALUE)
+ result->options[option_index].has_arg = 2;
+ else if (table[tabindex]->flag & ARG_HASVALUE)
+ result->options[option_index].has_arg = 1;
+ else
+ result->options[option_index].has_arg = 0;
+
+ option_index++;
+ }
+ }
+ /* terminate the options array with a zero-filled entry */
+ result->options[option_index].name = 0;
+ result->options[option_index].has_arg = 0;
+ result->options[option_index].flag = 0;
+ result->options[option_index].val = 0;
+ }
+
+ /*dump_longoptions(result);*/
+ return result;
+}
+
+static
+char * alloc_shortoptions(struct arg_hdr * *table)
+{
+ char *result;
+ size_t len = 2;
+ int tabindex;
+
+ /* determine the total number of option chars required */
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ struct arg_hdr *hdr = table[tabindex];
+ len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0);
+ }
+
+ result = malloc(len);
+ if (result)
+ {
+ char *res = result;
+
+ /* add a leading ':' so getopt return codes distinguish */
+ /* unrecognised option and options missing argument values */
+ *res++ = ':';
+
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ struct arg_hdr *hdr = table[tabindex];
+ const char *shortopts = hdr->shortopts;
+ while(shortopts && *shortopts)
+ {
+ *res++ = *shortopts++;
+ if (hdr->flag & ARG_HASVALUE)
+ *res++ = ':';
+ if (hdr->flag & ARG_HASOPTVALUE)
+ *res++ = ':';
+ }
+ }
+ /* null terminate the string */
+ *res = 0;
+ }
+
+ /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/
+ return result;
+}
+
+
+/* return index of the table terminator entry */
+static
+int arg_endindex(struct arg_hdr * *table)
+{
+ int tabindex = 0;
+ while (!(table[tabindex]->flag & ARG_TERMINATOR))
+ tabindex++;
+ return tabindex;
+}
+
+
+static
+void arg_parse_tagged(int argc,
+ char * *argv,
+ struct arg_hdr * *table,
+ struct arg_end *endtable)
+{
+ struct longoptions *longoptions;
+ char *shortoptions;
+ int copt;
+
+ /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
+
+ /* allocate short and long option arrays for the given opttable[]. */
+ /* if the allocs fail then put an error msg in the last table entry. */
+ longoptions = alloc_longoptions(table);
+ shortoptions = alloc_shortoptions(table);
+ if (!longoptions || !shortoptions)
+ {
+ /* one or both memory allocs failed */
+ arg_register_error(endtable, endtable, ARG_EMALLOC, NULL);
+ /* free anything that was allocated (this is null safe) */
+ free(shortoptions);
+ free(longoptions);
+ return;
+ }
+
+ /*dump_longoptions(longoptions);*/
+
+ /* reset getopts internal option-index to zero, and disable error reporting */
+ optind = 0;
+ opterr = 0;
+
+ /* fetch and process args using getopt_long */
+ while( (copt =
+ getopt_long(argc, argv, shortoptions, longoptions->options,
+ NULL)) != -1)
+ {
+ /*
+ printf("optarg='%s'\n",optarg);
+ printf("optind=%d\n",optind);
+ printf("copt=%c\n",(char)copt);
+ printf("optopt=%c (%d)\n",optopt, (int)(optopt));
+ */
+ switch(copt)
+ {
+ case 0:
+ {
+ int tabindex = longoptions->getoptval;
+ void *parent = table[tabindex]->parent;
+ /*printf("long option detected from argtable[%d]\n", tabindex);*/
+ if (optarg && optarg[0] == 0 &&
+ (table[tabindex]->flag & ARG_HASVALUE))
+ {
+ /* printf(": long option %s requires an argument\n",argv[optind-1]); */
+ arg_register_error(endtable, endtable, ARG_EMISSARG,
+ argv[optind - 1]);
+ /* continue to scan the (empty) argument value to enforce argument count checking */
+ }
+ if (table[tabindex]->scanfn)
+ {
+ int errorcode = table[tabindex]->scanfn(parent, optarg);
+ if (errorcode != 0)
+ arg_register_error(endtable, parent, errorcode, optarg);
+ }
+ }
+ break;
+
+ case '?':
+ /*
+ * getopt_long() found an unrecognised short option.
+ * if it was a short option its value is in optopt
+ * if it was a long option then optopt=0
+ */
+ switch (optopt)
+ {
+ case 0:
+ /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/
+ arg_register_error(endtable, endtable, ARG_ELONGOPT,
+ argv[optind - 1]);
+ break;
+ default:
+ /*printf("?* unrecognised short option '%c'\n",optopt);*/
+ arg_register_error(endtable, endtable, optopt, NULL);
+ break;
+ }
+ break;
+
+ case ':':
+ /*
+ * getopt_long() found an option with its argument missing.
+ */
+ /*printf(": option %s requires an argument\n",argv[optind-1]); */
+ arg_register_error(endtable, endtable, ARG_EMISSARG,
+ argv[optind - 1]);
+ break;
+
+ default:
+ {
+ /* getopt_long() found a valid short option */
+ int tabindex = find_shortoption(table, (char)copt);
+ /*printf("short option detected from argtable[%d]\n", tabindex);*/
+ if (tabindex == -1)
+ {
+ /* should never get here - but handle it just in case */
+ /*printf("unrecognised short option %d\n",copt);*/
+ arg_register_error(endtable, endtable, copt, NULL);
+ }
+ else
+ {
+ if (table[tabindex]->scanfn)
+ {
+ void *parent = table[tabindex]->parent;
+ int errorcode = table[tabindex]->scanfn(parent, optarg);
+ if (errorcode != 0)
+ arg_register_error(endtable, parent, errorcode, optarg);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ free(shortoptions);
+ free(longoptions);
+}
+
+
+static
+void arg_parse_untagged(int argc,
+ char * *argv,
+ struct arg_hdr * *table,
+ struct arg_end *endtable)
+{
+ int tabindex = 0;
+ int errorlast = 0;
+ const char *optarglast = NULL;
+ void *parentlast = NULL;
+
+ /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
+ while (!(table[tabindex]->flag & ARG_TERMINATOR))
+ {
+ void *parent;
+ int errorcode;
+
+ /* if we have exhausted our argv[optind] entries then we have finished */
+ if (optind >= argc)
+ {
+ /*printf("arg_parse_untagged(): argv[] exhausted\n");*/
+ return;
+ }
+
+ /* skip table entries with non-null long or short options (they are not untagged entries) */
+ if (table[tabindex]->longopts || table[tabindex]->shortopts)
+ {
+ /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/
+ tabindex++;
+ continue;
+ }
+
+ /* skip table entries with NULL scanfn */
+ if (!(table[tabindex]->scanfn))
+ {
+ /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/
+ tabindex++;
+ continue;
+ }
+
+ /* attempt to scan the current argv[optind] with the current */
+ /* table[tabindex] entry. If it succeeds then keep it, otherwise */
+ /* try again with the next table[] entry. */
+ parent = table[tabindex]->parent;
+ errorcode = table[tabindex]->scanfn(parent, argv[optind]);
+ if (errorcode == 0)
+ {
+ /* success, move onto next argv[optind] but stay with same table[tabindex] */
+ /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/
+ optind++;
+
+ /* clear the last tentative error */
+ errorlast = 0;
+ }
+ else
+ {
+ /* failure, try same argv[optind] with next table[tabindex] entry */
+ /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/
+ tabindex++;
+
+ /* remember this as a tentative error we may wish to reinstate later */
+ errorlast = errorcode;
+ optarglast = argv[optind];
+ parentlast = parent;
+ }
+
+ }
+
+ /* if a tenative error still remains at this point then register it as a proper error */
+ if (errorlast)
+ {
+ arg_register_error(endtable, parentlast, errorlast, optarglast);
+ optind++;
+ }
+
+ /* only get here when not all argv[] entries were consumed */
+ /* register an error for each unused argv[] entry */
+ while (optind < argc)
+ {
+ /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/
+ arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]);
+ }
+
+ return;
+}
+
+
+static
+void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable)
+{
+ int tabindex = 0;
+ /* printf("arg_parse_check()\n"); */
+ do
+ {
+ if (table[tabindex]->checkfn)
+ {
+ void *parent = table[tabindex]->parent;
+ int errorcode = table[tabindex]->checkfn(parent);
+ if (errorcode != 0)
+ arg_register_error(endtable, parent, errorcode, NULL);
+ }
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+}
+
+
+static
+void arg_reset(void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex = 0;
+ /*printf("arg_reset(%p)\n",argtable);*/
+ do
+ {
+ if (table[tabindex]->resetfn)
+ table[tabindex]->resetfn(table[tabindex]->parent);
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+}
+
+
+int arg_parse(int argc, char * *argv, void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ struct arg_end *endtable;
+ int endindex;
+ char * *argvcopy = NULL;
+
+ /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/
+
+ /* reset any argtable data from previous invocations */
+ arg_reset(argtable);
+
+ /* locate the first end-of-table marker within the array */
+ endindex = arg_endindex(table);
+ endtable = (struct arg_end *)table[endindex];
+
+ /* Special case of argc==0. This can occur on Texas Instruments DSP. */
+ /* Failure to trap this case results in an unwanted NULL result from */
+ /* the malloc for argvcopy (next code block). */
+ if (argc == 0)
+ {
+ /* We must still perform post-parse checks despite the absence of command line arguments */
+ arg_parse_check(table, endtable);
+
+ /* Now we are finished */
+ return endtable->count;
+ }
+
+ argvcopy = (char **)malloc(sizeof(char *) * (argc + 1));
+ if (argvcopy)
+ {
+ int i;
+
+ /*
+ Fill in the local copy of argv[]. We need a local copy
+ because getopt rearranges argv[] which adversely affects
+ susbsequent parsing attempts.
+ */
+ for (i = 0; i < argc; i++)
+ argvcopy[i] = argv[i];
+
+ argvcopy[argc] = NULL;
+
+ /* parse the command line (local copy) for tagged options */
+ arg_parse_tagged(argc, argvcopy, table, endtable);
+
+ /* parse the command line (local copy) for untagged options */
+ arg_parse_untagged(argc, argvcopy, table, endtable);
+
+ /* if no errors so far then perform post-parse checks otherwise dont bother */
+ if (endtable->count == 0)
+ arg_parse_check(table, endtable);
+
+ /* release the local copt of argv[] */
+ free(argvcopy);
+ }
+ else
+ {
+ /* memory alloc failed */
+ arg_register_error(endtable, endtable, ARG_EMALLOC, NULL);
+ }
+
+ return endtable->count;
+}
+
+
+/*
+ * Concatenate contents of src[] string onto *pdest[] string.
+ * The *pdest pointer is altered to point to the end of the
+ * target string and *pndest is decremented by the same number
+ * of chars.
+ * Does not append more than *pndest chars into *pdest[]
+ * so as to prevent buffer overruns.
+ * Its something like strncat() but more efficient for repeated
+ * calls on the same destination string.
+ * Example of use:
+ * char dest[30] = "good"
+ * size_t ndest = sizeof(dest);
+ * char *pdest = dest;
+ * arg_char(&pdest,"bye ",&ndest);
+ * arg_char(&pdest,"cruel ",&ndest);
+ * arg_char(&pdest,"world!",&ndest);
+ * Results in:
+ * dest[] == "goodbye cruel world!"
+ * ndest == 10
+ */
+static
+void arg_cat(char * *pdest, const char *src, size_t *pndest)
+{
+ char *dest = *pdest;
+ char *end = dest + *pndest;
+
+ /*locate null terminator of dest string */
+ while(dest < end && *dest != 0)
+ dest++;
+
+ /* concat src string to dest string */
+ while(dest < end && *src != 0)
+ *dest++ = *src++;
+
+ /* null terminate dest string */
+ *dest = 0;
+
+ /* update *pdest and *pndest */
+ *pndest = end - dest;
+ *pdest = dest;
+}
+
+
+static
+void arg_cat_option(char *dest,
+ size_t ndest,
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int optvalue)
+{
+ if (shortopts)
+ {
+ char option[3];
+
+ /* note: option array[] is initialiazed dynamically here to satisfy */
+ /* a deficiency in the watcom compiler wrt static array initializers. */
+ option[0] = '-';
+ option[1] = shortopts[0];
+ option[2] = 0;
+
+ arg_cat(&dest, option, &ndest);
+ if (datatype)
+ {
+ arg_cat(&dest, " ", &ndest);
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+ }
+ else if (longopts)
+ {
+ size_t ncspn;
+
+ /* add "--" tag prefix */
+ arg_cat(&dest, "--", &ndest);
+
+ /* add comma separated option tag */
+ ncspn = strcspn(longopts, ",");
+#ifdef __STDC_WANT_SECURE_LIB__
+ strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest);
+#else
+ strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest);
+#endif
+
+ if (datatype)
+ {
+ arg_cat(&dest, "=", &ndest);
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+ }
+ else if (datatype)
+ {
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+}
+
+static
+void arg_cat_optionv(char *dest,
+ size_t ndest,
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int optvalue,
+ const char *separator)
+{
+ separator = separator ? separator : "";
+
+ if (shortopts)
+ {
+ const char *c = shortopts;
+ while(*c)
+ {
+ /* "-a|-b|-c" */
+ char shortopt[3];
+
+ /* note: shortopt array[] is initialiazed dynamically here to satisfy */
+ /* a deficiency in the watcom compiler wrt static array initializers. */
+ shortopt[0] = '-';
+ shortopt[1] = *c;
+ shortopt[2] = 0;
+
+ arg_cat(&dest, shortopt, &ndest);
+ if (*++c)
+ arg_cat(&dest, separator, &ndest);
+ }
+ }
+
+ /* put separator between long opts and short opts */
+ if (shortopts && longopts)
+ arg_cat(&dest, separator, &ndest);
+
+ if (longopts)
+ {
+ const char *c = longopts;
+ while(*c)
+ {
+ size_t ncspn;
+
+ /* add "--" tag prefix */
+ arg_cat(&dest, "--", &ndest);
+
+ /* add comma separated option tag */
+ ncspn = strcspn(c, ",");
+#ifdef __STDC_WANT_SECURE_LIB__
+ strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest);
+#else
+ strncat(dest, c, (ncspn < ndest) ? ncspn : ndest);
+#endif
+ c += ncspn;
+
+ /* add given separator in place of comma */
+ if (*c == ',')
+ {
+ arg_cat(&dest, separator, &ndest);
+ c++;
+ }
+ }
+ }
+
+ if (datatype)
+ {
+ if (longopts)
+ arg_cat(&dest, "=", &ndest);
+ else if (shortopts)
+ arg_cat(&dest, " ", &ndest);
+
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+}
+
+
+/* this function should be deprecated because it doesnt consider optional argument values (ARG_HASOPTVALUE) */
+void arg_print_option(FILE *fp,
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *suffix)
+{
+ char syntax[200] = "";
+ suffix = suffix ? suffix : "";
+
+ /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ 0,
+ "|");
+
+ fputs(syntax, fp);
+ fputs(suffix, fp);
+}
+
+
+/*
+ * Print a GNU style [OPTION] string in which all short options that
+ * do not take argument values are presented in abbreviated form, as
+ * in: -xvfsd, or -xvf[sd], or [-xvsfd]
+ */
+static
+void arg_print_gnuswitch(FILE *fp, struct arg_hdr * *table)
+{
+ int tabindex;
+ char *format1 = " -%c";
+ char *format2 = " [-%c";
+ char *suffix = "";
+
+ /* print all mandatory switches that are without argument values */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ /* skip optional options */
+ if (table[tabindex]->mincount < 1)
+ continue;
+
+ /* skip non-short options */
+ if (table[tabindex]->shortopts == NULL)
+ continue;
+
+ /* skip options that take argument values */
+ if (table[tabindex]->flag & ARG_HASVALUE)
+ continue;
+
+ /* print the short option (only the first short option char, ignore multiple choices)*/
+ fprintf(fp, format1, table[tabindex]->shortopts[0]);
+ format1 = "%c";
+ format2 = "[%c";
+ }
+
+ /* print all optional switches that are without argument values */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ /* skip mandatory args */
+ if (table[tabindex]->mincount > 0)
+ continue;
+
+ /* skip args without short options */
+ if (table[tabindex]->shortopts == NULL)
+ continue;
+
+ /* skip args with values */
+ if (table[tabindex]->flag & ARG_HASVALUE)
+ continue;
+
+ /* print first short option */
+ fprintf(fp, format2, table[tabindex]->shortopts[0]);
+ format2 = "%c";
+ suffix = "]";
+ }
+
+ fprintf(fp, "%s", suffix);
+}
+
+
+void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int i, tabindex;
+
+ /* print GNU style [OPTION] string */
+ arg_print_gnuswitch(fp, table);
+
+ /* print remaining options in abbreviated style */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ char syntax[200] = "";
+ const char *shortopts, *longopts, *datatype;
+
+ /* skip short options without arg values (they were printed by arg_print_gnu_switch) */
+ if (table[tabindex]->shortopts &&
+ !(table[tabindex]->flag & ARG_HASVALUE))
+ continue;
+
+ shortopts = table[tabindex]->shortopts;
+ longopts = table[tabindex]->longopts;
+ datatype = table[tabindex]->datatype;
+ arg_cat_option(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE);
+
+ if (strlen(syntax) > 0)
+ {
+ /* print mandatory instances of this option */
+ for (i = 0; i < table[tabindex]->mincount; i++)
+ fprintf(fp, " %s", syntax);
+
+ /* print optional instances enclosed in "[..]" */
+ switch ( table[tabindex]->maxcount - table[tabindex]->mincount )
+ {
+ case 0:
+ break;
+ case 1:
+ fprintf(fp, " [%s]", syntax);
+ break;
+ case 2:
+ fprintf(fp, " [%s] [%s]", syntax, syntax);
+ break;
+ default:
+ fprintf(fp, " [%s]...", syntax);
+ break;
+ }
+ }
+ }
+
+ if (suffix)
+ fprintf(fp, "%s", suffix);
+}
+
+
+void arg_print_syntaxv(FILE *fp, void * *argtable, const char *suffix)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int i, tabindex;
+
+ /* print remaining options in abbreviated style */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ char syntax[200] = "";
+ const char *shortopts, *longopts, *datatype;
+
+ shortopts = table[tabindex]->shortopts;
+ longopts = table[tabindex]->longopts;
+ datatype = table[tabindex]->datatype;
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE,
+ "|");
+
+ /* print mandatory options */
+ for (i = 0; i < table[tabindex]->mincount; i++)
+ fprintf(fp, " %s", syntax);
+
+ /* print optional args enclosed in "[..]" */
+ switch ( table[tabindex]->maxcount - table[tabindex]->mincount )
+ {
+ case 0:
+ break;
+ case 1:
+ fprintf(fp, " [%s]", syntax);
+ break;
+ case 2:
+ fprintf(fp, " [%s] [%s]", syntax, syntax);
+ break;
+ default:
+ fprintf(fp, " [%s]...", syntax);
+ break;
+ }
+ }
+
+ if (suffix)
+ fprintf(fp, "%s", suffix);
+}
+
+
+void arg_print_glossary(FILE *fp, void * *argtable, const char *format)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex;
+
+ format = format ? format : " %-20s %s\n";
+ for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ if (table[tabindex]->glossary)
+ {
+ char syntax[200] = "";
+ const char *shortopts = table[tabindex]->shortopts;
+ const char *longopts = table[tabindex]->longopts;
+ const char *datatype = table[tabindex]->datatype;
+ const char *glossary = table[tabindex]->glossary;
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE,
+ ", ");
+ fprintf(fp, format, syntax, glossary);
+ }
+ }
+}
+
+
+/**
+ * Print a piece of text formatted, which means in a column with a
+ * left and a right margin. The lines are wrapped at whitspaces next
+ * to right margin. The function does not indent the first line, but
+ * only the following ones.
+ *
+ * Example:
+ * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." )
+ * will result in the following output:
+ *
+ * Some
+ * text
+ * that
+ * doesn'
+ * t fit.
+ *
+ * Too long lines will be wrapped in the middle of a word.
+ *
+ * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." )
+ * will result in the following output:
+ *
+ * Some
+ * text
+ * that
+ * doesn'
+ * t fit.
+ *
+ * As you see, the first line is not indented. This enables output of
+ * lines, which start in a line where output already happened.
+ *
+ * Author: Uli Fouquet
+ */
+static
+void arg_print_formatted( FILE *fp,
+ const unsigned lmargin,
+ const unsigned rmargin,
+ const char *text )
+{
+ const unsigned textlen = (unsigned)strlen( text );
+ unsigned line_start = 0;
+ unsigned line_end = textlen + 1;
+ const unsigned colwidth = (rmargin - lmargin) + 1;
+
+ /* Someone doesn't like us... */
+ if ( line_end < line_start )
+ { fprintf( fp, "%s\n", text ); }
+
+ while (line_end - 1 > line_start )
+ {
+ /* Eat leading whitespaces. This is essential because while
+ wrapping lines, there will often be a whitespace at beginning
+ of line */
+ while ( ISSPACE(*(text + line_start)) )
+ { line_start++; }
+
+ if ((line_end - line_start) > colwidth )
+ { line_end = line_start + colwidth; }
+
+ /* Find last whitespace, that fits into line */
+ while ( ( line_end > line_start )
+ && ( line_end - line_start > colwidth )
+ && !ISSPACE(*(text + line_end)))
+ { line_end--; }
+
+ /* Do not print trailing whitespace. If this text
+ has got only one line, line_end now points to the
+ last char due to initialization. */
+ line_end--;
+
+ /* Output line of text */
+ while ( line_start < line_end )
+ {
+ fputc(*(text + line_start), fp );
+ line_start++;
+ }
+ fputc( '\n', fp );
+
+ /* Initialize another line */
+ if ( line_end + 1 < textlen )
+ {
+ unsigned i;
+
+ for (i = 0; i < lmargin; i++ )
+ { fputc( ' ', fp ); }
+
+ line_end = textlen;
+ }
+
+ /* If we have to print another line, get also the last char. */
+ line_end++;
+
+ } /* lines of text */
+}
+
+/**
+ * Prints the glossary in strict GNU format.
+ * Differences to arg_print_glossary() are:
+ * - wraps lines after 80 chars
+ * - indents lines without shortops
+ * - does not accept formatstrings
+ *
+ * Contributed by Uli Fouquet
+ */
+void arg_print_glossary_gnu(FILE *fp, void * *argtable )
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex;
+
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ if (table[tabindex]->glossary)
+ {
+ char syntax[200] = "";
+ const char *shortopts = table[tabindex]->shortopts;
+ const char *longopts = table[tabindex]->longopts;
+ const char *datatype = table[tabindex]->datatype;
+ const char *glossary = table[tabindex]->glossary;
+
+ if ( !shortopts && longopts )
+ {
+ /* Indent trailing line by 4 spaces... */
+ memset( syntax, ' ', 4 );
+ *(syntax + 4) = '\0';
+ }
+
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE,
+ ", ");
+
+ /* If syntax fits not into column, print glossary in new line... */
+ if ( strlen(syntax) > 25 )
+ {
+ fprintf( fp, " %-25s %s\n", syntax, "" );
+ *syntax = '\0';
+ }
+
+ fprintf( fp, " %-25s ", syntax );
+ arg_print_formatted( fp, 28, 79, glossary );
+ }
+ } /* for each table entry */
+
+ fputc( '\n', fp );
+}
+
+
+/**
+ * Checks the argtable[] array for NULL entries and returns 1
+ * if any are found, zero otherwise.
+ */
+int arg_nullcheck(void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex;
+ /*printf("arg_nullcheck(%p)\n",argtable);*/
+
+ if (!table)
+ return 1;
+
+ tabindex = 0;
+ do
+ {
+ /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/
+ if (!table[tabindex])
+ return 1;
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+
+ return 0;
+}
+
+
+/*
+ * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design.
+ * The flaw results in memory leak in the (very rare) case that an intermediate
+ * entry in the argtable array failed its memory allocation while others following
+ * that entry were still allocated ok. Those subsequent allocations will not be
+ * deallocated by arg_free().
+ * Despite the unlikeliness of the problem occurring, and the even unlikelier event
+ * that it has any deliterious effect, it is fixed regardless by replacing arg_free()
+ * with the newer arg_freetable() function.
+ * We still keep arg_free() for backwards compatibility.
+ */
+void arg_free(void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex = 0;
+ int flag;
+ /*printf("arg_free(%p)\n",argtable);*/
+ do
+ {
+ /*
+ if we encounter a NULL entry then somewhat incorrectly we presume
+ we have come to the end of the array. It isnt strictly true because
+ an intermediate entry could be NULL with other non-NULL entries to follow.
+ The subsequent argtable entries would then not be freed as they should.
+ */
+ if (table[tabindex] == NULL)
+ break;
+
+ flag = table[tabindex]->flag;
+ free(table[tabindex]);
+ table[tabindex++] = NULL;
+
+ } while(!(flag & ARG_TERMINATOR));
+}
+
+/* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */
+void arg_freetable(void * *argtable, size_t n)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ size_t tabindex = 0;
+ /*printf("arg_freetable(%p)\n",argtable);*/
+ for (tabindex = 0; tabindex < n; tabindex++)
+ {
+ if (table[tabindex] == NULL)
+ continue;
+
+ free(table[tabindex]);
+ table[tabindex] = NULL;
+ };
+}
+
diff --git a/src/argtable3.h b/src/argtable3.h
new file mode 100644
index 0000000..1107de2
--- /dev/null
+++ b/src/argtable3.h
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ * <sheitmann@users.sourceforge.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#ifndef ARGTABLE3
+#define ARGTABLE3
+
+#include <stdio.h> /* FILE */
+#include <time.h> /* struct tm */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARG_REX_ICASE 1
+
+/* bit masks for arg_hdr.flag */
+enum
+{
+ ARG_TERMINATOR=0x1,
+ ARG_HASVALUE=0x2,
+ ARG_HASOPTVALUE=0x4
+};
+
+typedef void (arg_resetfn)(void *parent);
+typedef int (arg_scanfn)(void *parent, const char *argval);
+typedef int (arg_checkfn)(void *parent);
+typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname);
+
+
+/*
+* The arg_hdr struct defines properties that are common to all arg_xxx structs.
+* The argtable library requires each arg_xxx struct to have an arg_hdr
+* struct as its first data member.
+* The argtable library functions then use this data to identify the
+* properties of the command line option, such as its option tags,
+* datatype string, and glossary strings, and so on.
+* Moreover, the arg_hdr struct contains pointers to custom functions that
+* are provided by each arg_xxx struct which perform the tasks of parsing
+* that particular arg_xxx arguments, performing post-parse checks, and
+* reporting errors.
+* These functions are private to the individual arg_xxx source code
+* and are the pointer to them are initiliased by that arg_xxx struct's
+* constructor function. The user could alter them after construction
+* if desired, but the original intention is for them to be set by the
+* constructor and left unaltered.
+*/
+struct arg_hdr
+{
+ char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
+ const char *shortopts; /* String defining the short options */
+ const char *longopts; /* String defiing the long options */
+ const char *datatype; /* Description of the argument data type */
+ const char *glossary; /* Description of the option as shown by arg_print_glossary function */
+ int mincount; /* Minimum number of occurences of this option accepted */
+ int maxcount; /* Maximum number of occurences if this option accepted */
+ void *parent; /* Pointer to parent arg_xxx struct */
+ arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */
+ arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */
+ arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */
+ arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */
+ void *priv; /* Pointer to private header data for use by arg_xxx functions */
+};
+
+struct arg_rem
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+};
+
+struct arg_lit
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+};
+
+struct arg_int
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ int *ival; /* Array of parsed argument values */
+};
+
+struct arg_dbl
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ double *dval; /* Array of parsed argument values */
+};
+
+struct arg_str
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ const char **sval; /* Array of parsed argument values */
+};
+
+struct arg_rex
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ const char **sval; /* Array of parsed argument values */
+};
+
+struct arg_file
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args*/
+ const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */
+ const char **basename; /* Array of parsed basenames (eg: foo.bar) */
+ const char **extension; /* Array of parsed extensions (eg: .bar) */
+};
+
+struct arg_date
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ const char *format; /* strptime format string used to parse the date */
+ int count; /* Number of matching command line args */
+ struct tm *tmval; /* Array of parsed time values */
+};
+
+enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG};
+struct arg_end
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of errors encountered */
+ int *error; /* Array of error codes */
+ void **parent; /* Array of pointers to offending arg_xxx struct */
+ const char **argval; /* Array of pointers to offending argv[] string */
+};
+
+
+/**** arg_xxx constructor functions *********************************/
+
+struct arg_rem* arg_rem(const char* datatype, const char* glossary);
+
+struct arg_lit* arg_lit0(const char* shortopts,
+ const char* longopts,
+ const char* glossary);
+struct arg_lit* arg_lit1(const char* shortopts,
+ const char* longopts,
+ const char *glossary);
+struct arg_lit* arg_litn(const char* shortopts,
+ const char* longopts,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_key* arg_key0(const char* keyword,
+ int flags,
+ const char* glossary);
+struct arg_key* arg_key1(const char* keyword,
+ int flags,
+ const char* glossary);
+struct arg_key* arg_keyn(const char* keyword,
+ int flags,
+ int mincount,
+ int maxcount,
+ const char* glossary);
+
+struct arg_int* arg_int0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_int* arg_int1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_int* arg_intn(const char* shortopts,
+ const char* longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_dbl* arg_dbl0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_dbl* arg_dbl1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_dbl* arg_dbln(const char* shortopts,
+ const char* longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_str* arg_str0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_str* arg_str1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_str* arg_strn(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_rex* arg_rex0(const char* shortopts,
+ const char* longopts,
+ const char* pattern,
+ const char* datatype,
+ int flags,
+ const char* glossary);
+struct arg_rex* arg_rex1(const char* shortopts,
+ const char* longopts,
+ const char* pattern,
+ const char* datatype,
+ int flags,
+ const char *glossary);
+struct arg_rex* arg_rexn(const char* shortopts,
+ const char* longopts,
+ const char* pattern,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ int flags,
+ const char *glossary);
+
+struct arg_file* arg_file0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_file* arg_file1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_file* arg_filen(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_date* arg_date0(const char* shortopts,
+ const char* longopts,
+ const char* format,
+ const char* datatype,
+ const char* glossary);
+struct arg_date* arg_date1(const char* shortopts,
+ const char* longopts,
+ const char* format,
+ const char* datatype,
+ const char *glossary);
+struct arg_date* arg_daten(const char* shortopts,
+ const char* longopts,
+ const char* format,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_end* arg_end(int maxerrors);
+
+
+/**** other functions *******************************************/
+int arg_nullcheck(void **argtable);
+int arg_parse(int argc, char **argv, void **argtable);
+void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix);
+void arg_print_syntax(FILE *fp, void **argtable, const char *suffix);
+void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix);
+void arg_print_glossary(FILE *fp, void **argtable, const char *format);
+void arg_print_glossary_gnu(FILE *fp, void **argtable);
+void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
+void arg_freetable(void **argtable, size_t n);
+
+/**** deprecated functions, for back-compatibility only ********/
+void arg_free(void **argtable);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/base64.c b/src/base64.c
new file mode 100644
index 0000000..d07ea60
--- /dev/null
+++ b/src/base64.c
@@ -0,0 +1,253 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "base64.h"
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 76; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ out = malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 76) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = count / 4 * 3;
+ pos = out = malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ *out_len = pos - out;
+ return out;
+}
+
+unsigned char * b64t_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ // allocate new buffer
+ unsigned char *out = malloc(len);
+ if (out == NULL)
+ return NULL;
+ memset(out, 0, len);
+
+ // remove new line
+ size_t index = 0;
+ size_t new_index = 0;
+ for (index =0; index < len; index++)
+ {
+ if(src[index] != '\n' &&
+ src[index] != '\r' &&
+ src[index] != '=' &&
+ src[index] >= '+' &&
+ src[index] <= 'z' ) {
+ out[new_index] = src[index];
+ new_index ++;
+ }
+ }
+ out[new_index] = 0;
+ *out_len = new_index;
+
+ for (size_t i=0; i < *out_len; i++) {
+ if(out[i] == '+') {
+ out[i] = '-';
+ }
+ }
+ for (size_t i=0; i < *out_len; i++) {
+ if(out[i] == '/') {
+ out[i] = '_';
+ }
+ }
+ return out;
+}
+
+unsigned char * b64t_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ size_t index = 0;
+ size_t nb_encoding_chars = 0;
+ for (index=0; index < len; index++) {
+ if(src[index] == '\n' ||
+ src[index] == '\r' ||
+ src[index] == '=') {
+ nb_encoding_chars++;
+ }
+ }
+
+ size_t new_len = len - nb_encoding_chars;
+ size_t nb_newlines = new_len / 76;
+ size_t padding_len = 0;
+ if (new_len % 4 != 0) {
+ padding_len = 4 - new_len % 4;
+ }
+ *out_len = new_len + nb_newlines + padding_len + 1;
+
+ // allocate new buffer
+ unsigned char *out = malloc(*out_len);
+ if (out == NULL)
+ return NULL;
+ memset(out, 0, len);
+
+ size_t index_out = 0;
+ size_t index_data = 0;
+ for(index = 0; index < len; index++) {
+ if(src[index] != '\n' &&
+ src[index] != '\r' &&
+ src[index] != '=') {
+ if(index_data != 0 && index_data % 76 == 0) {
+ out[index_out] = '\n';
+ index_out++;
+ }
+ if (src[index] == '-') {
+ out[index_out] = '+';
+ }
+ else if (src[index] == '_') {
+ out[index_out] = '/';
+ }
+ else {
+ out[index_out] = src[index];
+ }
+ index_out++;
+ index_data++;
+ }
+ }
+ for(index = 0; index < padding_len; index ++) {
+ out[index_out] = '=';
+ index_out++;
+ }
+ out[index_out] = '\n';
+
+ return out;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 0000000..b2a6753
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,21 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char * b64t_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char * b64t_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/src/binhex.c b/src/binhex.c
new file mode 100644
index 0000000..f07aa45
--- /dev/null
+++ b/src/binhex.c
@@ -0,0 +1,67 @@
+// From https://nachtimwald.com/2017/09/24/hex-encode-and-decode-in-c/
+
+#include <stdlib.h>
+#include <string.h>
+#include "binhex.h"
+
+char *bin2hex(const unsigned char *bin, size_t len)
+{
+ char *out;
+ size_t i;
+
+ if (bin == NULL || len == 0)
+ return NULL;
+
+ out = malloc(len*2+1);
+ for (i=0; i<len; i++) {
+ out[i*2] = "0123456789ABCDEF"[bin[i] >> 4];
+ out[i*2+1] = "0123456789ABCDEF"[bin[i] & 0x0F];
+ }
+ out[len*2] = '\0';
+
+ return out;
+}
+
+int hexchr2bin(const char hex, char *out)
+{
+ if (out == NULL)
+ return 0;
+
+ if (hex >= '0' && hex <= '9') {
+ *out = hex - '0';
+ } else if (hex >= 'A' && hex <= 'F') {
+ *out = hex - 'A' + 10;
+ } else if (hex >= 'a' && hex <= 'f') {
+ *out = hex - 'a' + 10;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+size_t hexs2bin(const char *hex, unsigned char **out)
+{
+ size_t len;
+ char b1;
+ char b2;
+ size_t i;
+
+ if (hex == NULL || *hex == '\0' || out == NULL)
+ return 0;
+
+ len = strlen(hex);
+ if (len % 2 != 0)
+ return 0;
+ len /= 2;
+
+ *out = malloc(len);
+ memset(*out, 'A', len);
+ for (i=0; i<len; i++) {
+ if (!hexchr2bin(hex[i*2], &b1) || !hexchr2bin(hex[i*2+1], &b2)) {
+ return 0;
+ }
+ (*out)[i] = (b1 << 4) | b2;
+ }
+ return len;
+}
diff --git a/src/binhex.h b/src/binhex.h
new file mode 100644
index 0000000..71348db
--- /dev/null
+++ b/src/binhex.h
@@ -0,0 +1,9 @@
+#ifndef BINHEX_H
+#define BINHEX_H
+
+char *bin2hex(const unsigned char *bin, size_t len);
+int hexchr2bin(const char hex, char *out);
+size_t hexs2bin(const char *hex, unsigned char **out);
+
+#endif /* BINHEX_H */
+
diff --git a/src/blake2-impl.h b/src/blake2-impl.h
new file mode 100644
index 0000000..e6cdf7c
--- /dev/null
+++ b/src/blake2-impl.h
@@ -0,0 +1,90 @@
+#ifndef ARGON2_BLAKE2_IMPL_H
+#define ARGON2_BLAKE2_IMPL_H
+
+#include <stdint.h>
+
+/* Argon2 Team - Begin Code */
+/*
+ Not an exhaustive list, but should cover the majority of modern platforms
+ Additionally, the code will always be correct---this is only a performance
+ tweak.
+*/
+#if (defined(__BYTE_ORDER__) && \
+ (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
+ defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
+ defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
+ defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
+ defined(_M_ARM)
+#define NATIVE_LITTLE_ENDIAN
+#endif
+/* Argon2 Team - End Code */
+
+static inline uint32_t load32(const void *src) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ return *(const uint32_t *)src;
+#else
+ const uint8_t *p = (const uint8_t *)src;
+ uint32_t w = *p++;
+ w |= (uint32_t)(*p++) << 8;
+ w |= (uint32_t)(*p++) << 16;
+ w |= (uint32_t)(*p++) << 24;
+ return w;
+#endif
+}
+
+static inline uint64_t load64(const void *src) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ return *(const uint64_t *)src;
+#else
+ const uint8_t *p = (const uint8_t *)src;
+ uint64_t w = *p++;
+ w |= (uint64_t)(*p++) << 8;
+ w |= (uint64_t)(*p++) << 16;
+ w |= (uint64_t)(*p++) << 24;
+ w |= (uint64_t)(*p++) << 32;
+ w |= (uint64_t)(*p++) << 40;
+ w |= (uint64_t)(*p++) << 48;
+ w |= (uint64_t)(*p++) << 56;
+ return w;
+#endif
+}
+
+static inline void store32(void *dst, uint32_t w) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ *(uint32_t *)dst = w;
+#else
+ uint8_t *p = (uint8_t *)dst;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+#endif
+}
+
+static inline void store64(void *dst, uint64_t w) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ *(uint64_t *)dst = w;
+#else
+ uint8_t *p = (uint8_t *)dst;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+#endif
+}
+
+#endif // ARGON2_BLAKE2_IMPL_H
diff --git a/src/blake2.c b/src/blake2.c
new file mode 100644
index 0000000..db0159e
--- /dev/null
+++ b/src/blake2.c
@@ -0,0 +1,225 @@
+#include <string.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "argon2-core.h"
+
+static const uint64_t blake2b_IV[8] = {
+ UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b),
+ UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1),
+ UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f),
+ UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)
+};
+
+#define rotr64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
+
+static const unsigned int blake2b_sigma[12][16] = {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
+ {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
+ {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
+ {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
+ {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
+ {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
+ {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
+ {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
+ {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
+};
+
+#define G(m, r, i, a, b, c, d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
+ d = rotr64(d ^ a, 32); \
+ c = c + d; \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
+ d = rotr64(d ^ a, 16); \
+ c = c + d; \
+ b = rotr64(b ^ c, 63); \
+ } while ((void)0, 0)
+
+#define ROUND(m, v, r) \
+ do { \
+ G(m, r, 0, v[0], v[4], v[ 8], v[12]); \
+ G(m, r, 1, v[1], v[5], v[ 9], v[13]); \
+ G(m, r, 2, v[2], v[6], v[10], v[14]); \
+ G(m, r, 3, v[3], v[7], v[11], v[15]); \
+ G(m, r, 4, v[0], v[5], v[10], v[15]); \
+ G(m, r, 5, v[1], v[6], v[11], v[12]); \
+ G(m, r, 6, v[2], v[7], v[ 8], v[13]); \
+ G(m, r, 7, v[3], v[4], v[ 9], v[14]); \
+ } while ((void)0, 0)
+
+void blake2b_compress(blake2b_state *S, const void *block, uint64_t f0)
+{
+ uint64_t m[16];
+ uint64_t v[16];
+
+ m[ 0] = load64((const uint64_t *)block + 0);
+ m[ 1] = load64((const uint64_t *)block + 1);
+ m[ 2] = load64((const uint64_t *)block + 2);
+ m[ 3] = load64((const uint64_t *)block + 3);
+ m[ 4] = load64((const uint64_t *)block + 4);
+ m[ 5] = load64((const uint64_t *)block + 5);
+ m[ 6] = load64((const uint64_t *)block + 6);
+ m[ 7] = load64((const uint64_t *)block + 7);
+ m[ 8] = load64((const uint64_t *)block + 8);
+ m[ 9] = load64((const uint64_t *)block + 9);
+ m[10] = load64((const uint64_t *)block + 10);
+ m[11] = load64((const uint64_t *)block + 11);
+ m[12] = load64((const uint64_t *)block + 12);
+ m[13] = load64((const uint64_t *)block + 13);
+ m[14] = load64((const uint64_t *)block + 14);
+ m[15] = load64((const uint64_t *)block + 15);
+
+ v[ 0] = S->h[0];
+ v[ 1] = S->h[1];
+ v[ 2] = S->h[2];
+ v[ 3] = S->h[3];
+ v[ 4] = S->h[4];
+ v[ 5] = S->h[5];
+ v[ 6] = S->h[6];
+ v[ 7] = S->h[7];
+ v[ 8] = blake2b_IV[0];
+ v[ 9] = blake2b_IV[1];
+ v[10] = blake2b_IV[2];
+ v[11] = blake2b_IV[3];
+ v[12] = blake2b_IV[4] ^ S->t[0];
+ v[13] = blake2b_IV[5] ^ S->t[1];
+ v[14] = blake2b_IV[6] ^ f0;
+ v[15] = blake2b_IV[7];
+
+ ROUND(m, v, 0);
+ ROUND(m, v, 1);
+ ROUND(m, v, 2);
+ ROUND(m, v, 3);
+ ROUND(m, v, 4);
+ ROUND(m, v, 5);
+ ROUND(m, v, 6);
+ ROUND(m, v, 7);
+ ROUND(m, v, 8);
+ ROUND(m, v, 9);
+ ROUND(m, v, 10);
+ ROUND(m, v, 11);
+
+ S->h[0] ^= v[0] ^ v[ 8];
+ S->h[1] ^= v[1] ^ v[ 9];
+ S->h[2] ^= v[2] ^ v[10];
+ S->h[3] ^= v[3] ^ v[11];
+ S->h[4] ^= v[4] ^ v[12];
+ S->h[5] ^= v[5] ^ v[13];
+ S->h[6] ^= v[6] ^ v[14];
+ S->h[7] ^= v[7] ^ v[15];
+}
+
+static void blake2b_increment_counter(blake2b_state *S, uint64_t inc)
+{
+ S->t[0] += inc;
+ S->t[1] += (S->t[0] < inc);
+}
+
+static void blake2b_init_state(blake2b_state *S)
+{
+ memcpy(S->h, blake2b_IV, sizeof(S->h));
+ S->t[1] = S->t[0] = 0;
+ S->buflen = 0;
+}
+
+void blake2b_init(blake2b_state *S, size_t outlen)
+{
+ blake2b_init_state(S);
+ /* XOR initial state with param block: */
+ S->h[0] ^= (uint64_t)outlen | (UINT64_C(1) << 16) | (UINT64_C(1) << 24);
+}
+
+void blake2b_update(blake2b_state *S, const void *in, size_t inlen)
+{
+ const uint8_t *pin = (const uint8_t *)in;
+
+ if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) {
+ size_t left = S->buflen;
+ size_t fill = BLAKE2B_BLOCKBYTES - left;
+ memcpy(&S->buf[left], pin, fill);
+ blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
+ blake2b_compress(S, S->buf, 0);
+ S->buflen = 0;
+ inlen -= fill;
+ pin += fill;
+ /* Avoid buffer copies when possible */
+ while (inlen > BLAKE2B_BLOCKBYTES) {
+ blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
+ blake2b_compress(S, pin, 0);
+ inlen -= BLAKE2B_BLOCKBYTES;
+ pin += BLAKE2B_BLOCKBYTES;
+ }
+ }
+ memcpy(&S->buf[S->buflen], pin, inlen);
+ S->buflen += inlen;
+}
+
+void blake2b_final(blake2b_state *S, void *out, size_t outlen)
+{
+ uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
+ unsigned int i;
+
+ blake2b_increment_counter(S, S->buflen);
+ memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
+ blake2b_compress(S, S->buf, UINT64_C(0xFFFFFFFFFFFFFFFF));
+
+ for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
+ store64(buffer + i * sizeof(uint64_t), S->h[i]);
+ }
+
+ memcpy(out, buffer, outlen);
+ clear_internal_memory(buffer, sizeof(buffer));
+ clear_internal_memory(S->buf, sizeof(S->buf));
+ clear_internal_memory(S->h, sizeof(S->h));
+}
+
+void blake2b_long(void *out, size_t outlen, const void *in, size_t inlen)
+{
+ uint8_t *pout = (uint8_t *)out;
+ blake2b_state blake_state;
+ uint8_t outlen_bytes[sizeof(uint32_t)] = {0};
+
+ store32(outlen_bytes, (uint32_t)outlen);
+ if (outlen <= BLAKE2B_OUTBYTES) {
+ blake2b_init(&blake_state, outlen);
+ blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes));
+ blake2b_update(&blake_state, in, inlen);
+ blake2b_final(&blake_state, pout, outlen);
+ } else {
+ uint32_t toproduce;
+ uint8_t out_buffer[BLAKE2B_OUTBYTES];
+
+ blake2b_init(&blake_state, BLAKE2B_OUTBYTES);
+ blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes));
+ blake2b_update(&blake_state, in, inlen);
+ blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES);
+
+ memcpy(pout, out_buffer, BLAKE2B_OUTBYTES / 2);
+ pout += BLAKE2B_OUTBYTES / 2;
+ toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2;
+
+ while (toproduce > BLAKE2B_OUTBYTES) {
+ blake2b_init(&blake_state, BLAKE2B_OUTBYTES);
+ blake2b_update(&blake_state, out_buffer, BLAKE2B_OUTBYTES);
+ blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES);
+
+ memcpy(pout, out_buffer, BLAKE2B_OUTBYTES / 2);
+ pout += BLAKE2B_OUTBYTES / 2;
+ toproduce -= BLAKE2B_OUTBYTES / 2;
+ }
+
+ blake2b_init(&blake_state, toproduce);
+ blake2b_update(&blake_state, out_buffer, BLAKE2B_OUTBYTES);
+ blake2b_final(&blake_state, out_buffer, toproduce);
+
+ memcpy(pout, out_buffer, toproduce);
+
+ clear_internal_memory(out_buffer, sizeof(out_buffer));
+ }
+}
diff --git a/src/blake2.h b/src/blake2.h
new file mode 100644
index 0000000..7deeaa1
--- /dev/null
+++ b/src/blake2.h
@@ -0,0 +1,30 @@
+#ifndef ARGON2_BLAKE2_H
+#define ARGON2_BLAKE2_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+enum blake2b_constant {
+ BLAKE2B_BLOCKBYTES = 128,
+ BLAKE2B_OUTBYTES = 64,
+ BLAKE2B_KEYBYTES = 64,
+ BLAKE2B_SALTBYTES = 16,
+ BLAKE2B_PERSONALBYTES = 16
+};
+
+typedef struct __blake2b_state {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint8_t buf[BLAKE2B_BLOCKBYTES];
+ size_t buflen;
+} blake2b_state;
+
+/* Streaming API */
+void blake2b_init(blake2b_state *S, size_t outlen);
+void blake2b_update(blake2b_state *S, const void *in, size_t inlen);
+void blake2b_final(blake2b_state *S, void *out, size_t outlen);
+
+void blake2b_long(void *out, size_t outlen, const void *in, size_t inlen);
+
+#endif // ARGON2_BLAKE2_H
+
diff --git a/src/chacha20_drng.c b/src/chacha20_drng.c
new file mode 100644
index 0000000..9180557
--- /dev/null
+++ b/src/chacha20_drng.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2016 - 2017, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#endif
+
+
+#include "chacha20_drng.h"
+#include "randombytes.h"
+
+#define MAJVERSION 1 /* API / ABI incompatible changes,
+ * functional changes that require consumer
+ * to be updated (as long as this number is
+ * zero, the API is not considered stable
+ * and can change without a bump of the
+ * major version). */
+#define MINVERSION 3 /* API compatible, ABI may change,
+ * functional enhancements only, consumer
+ * can be left unchanged if enhancements are
+ * not considered. */
+#define PATCHLEVEL 2 /* API / ABI compatible, no functional
+ * changes, no enhancements, bug fixes
+ * only. */
+
+#define CHACHA20_DRNG_ALIGNMENT 8 /* allow u8 to u32 conversions */
+
+#if __GNUC__ >= 4
+# define DSO_PUBLIC __attribute__ ((visibility ("default")))
+#else
+# define DSO_PUBLIC
+#endif
+
+/*********************************** Helper ***********************************/
+
+#ifndef _WIN32
+#define min(x, y) ((x < y) ? x : y)
+#endif
+#define __aligned(x) __attribute__((aligned(x)))
+
+static inline void memset_secure(void *s, int c, uint32_t n)
+{
+ memset(s, c, n);
+ __asm__ __volatile__("" : : "r" (s) : "memory");
+}
+
+static inline void get_time(time_t *sec, uint32_t *nsec)
+{
+#ifdef _WIN32
+ SYSTEMTIME SystemTime;
+ GetSystemTime(&SystemTime);
+ if(sec)
+ *sec = SystemTime.wSecond;
+ if(nsec)
+ *nsec = SystemTime.wMilliseconds;
+#else
+ struct timespec time;
+ if (clock_gettime(CLOCK_REALTIME, &time) == 0) {
+ if (sec)
+ *sec = time.tv_sec;
+ if (nsec)
+ *nsec = time.tv_nsec;
+ }
+#endif
+
+}
+
+static inline uint32_t rol32(uint32_t x, int n)
+{
+ return ( (x << (n&(32-1))) | (x >> ((32-n)&(32-1))) );
+}
+
+
+/* Endian dependent byte swap operations. */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+/* Byte swap for 32-bit and 64-bit integers. */
+static inline uint32_t ror32(uint32_t x, int n)
+{
+ return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) );
+}
+static inline uint32_t _bswap32(uint32_t x)
+{
+ return ((rol32(x, 8) & 0x00ff00ffL) | (ror32(x, 8) & 0xff00ff00L));
+}
+# define le_bswap32(x) _bswap32(x)
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define le_bswap32(x) ((uint32_t)(x))
+#else
+#error "Endianess not defined"
+#endif
+
+/******************************* ChaCha20 Block *******************************/
+
+#define CHACHA20_KEY_SIZE 32
+#define CHACHA20_KEY_SIZE_WORDS (CHACHA20_KEY_SIZE / sizeof(uint32_t))
+
+/* State according to RFC 7539 section 2.3 */
+struct chacha20_state {
+ uint32_t constants[4];
+ union {
+ uint32_t u[CHACHA20_KEY_SIZE_WORDS];
+ uint8_t b[CHACHA20_KEY_SIZE];
+ } key;
+ uint32_t counter;
+ uint32_t nonce[3];
+};
+
+#define CHACHA20_BLOCK_SIZE sizeof(struct chacha20_state)
+#define CHACHA20_BLOCK_SIZE_WORDS (CHACHA20_BLOCK_SIZE / sizeof(uint32_t))
+
+/* ChaCha20 block function according to RFC 7539 section 2.3 */
+static void chacha20_block(uint32_t *state, uint32_t *stream)
+{
+ uint32_t i, ws[CHACHA20_BLOCK_SIZE_WORDS], *out = stream;
+
+ for (i = 0; i < CHACHA20_BLOCK_SIZE_WORDS; i++)
+ ws[i] = state[i];
+
+ for (i = 0; i < 10; i++) {
+ /* Quarterround 1 */
+ ws[0] += ws[4]; ws[12] = rol32(ws[12] ^ ws[0], 16);
+ ws[8] += ws[12]; ws[4] = rol32(ws[4] ^ ws[8], 12);
+ ws[0] += ws[4]; ws[12] = rol32(ws[12] ^ ws[0], 8);
+ ws[8] += ws[12]; ws[4] = rol32(ws[4] ^ ws[8], 7);
+
+ /* Quarterround 2 */
+ ws[1] += ws[5]; ws[13] = rol32(ws[13] ^ ws[1], 16);
+ ws[9] += ws[13]; ws[5] = rol32(ws[5] ^ ws[9], 12);
+ ws[1] += ws[5]; ws[13] = rol32(ws[13] ^ ws[1], 8);
+ ws[9] += ws[13]; ws[5] = rol32(ws[5] ^ ws[9], 7);
+
+ /* Quarterround 3 */
+ ws[2] += ws[6]; ws[14] = rol32(ws[14] ^ ws[2], 16);
+ ws[10] += ws[14]; ws[6] = rol32(ws[6] ^ ws[10], 12);
+ ws[2] += ws[6]; ws[14] = rol32(ws[14] ^ ws[2], 8);
+ ws[10] += ws[14]; ws[6] = rol32(ws[6] ^ ws[10], 7);
+
+ /* Quarterround 4 */
+ ws[3] += ws[7]; ws[15] = rol32(ws[15] ^ ws[3], 16);
+ ws[11] += ws[15]; ws[7] = rol32(ws[7] ^ ws[11], 12);
+ ws[3] += ws[7]; ws[15] = rol32(ws[15] ^ ws[3], 8);
+ ws[11] += ws[15]; ws[7] = rol32(ws[7] ^ ws[11], 7);
+
+ /* Quarterround 5 */
+ ws[0] += ws[5]; ws[15] = rol32(ws[15] ^ ws[0], 16);
+ ws[10] += ws[15]; ws[5] = rol32(ws[5] ^ ws[10], 12);
+ ws[0] += ws[5]; ws[15] = rol32(ws[15] ^ ws[0], 8);
+ ws[10] += ws[15]; ws[5] = rol32(ws[5] ^ ws[10], 7);
+
+ /* Quarterround 6 */
+ ws[1] += ws[6]; ws[12] = rol32(ws[12] ^ ws[1], 16);
+ ws[11] += ws[12]; ws[6] = rol32(ws[6] ^ ws[11], 12);
+ ws[1] += ws[6]; ws[12] = rol32(ws[12] ^ ws[1], 8);
+ ws[11] += ws[12]; ws[6] = rol32(ws[6] ^ ws[11], 7);
+
+ /* Quarterround 7 */
+ ws[2] += ws[7]; ws[13] = rol32(ws[13] ^ ws[2], 16);
+ ws[8] += ws[13]; ws[7] = rol32(ws[7] ^ ws[8], 12);
+ ws[2] += ws[7]; ws[13] = rol32(ws[13] ^ ws[2], 8);
+ ws[8] += ws[13]; ws[7] = rol32(ws[7] ^ ws[8], 7);
+
+ /* Quarterround 8 */
+ ws[3] += ws[4]; ws[14] = rol32(ws[14] ^ ws[3], 16);
+ ws[9] += ws[14]; ws[4] = rol32(ws[4] ^ ws[9], 12);
+ ws[3] += ws[4]; ws[14] = rol32(ws[14] ^ ws[3], 8);
+ ws[9] += ws[14]; ws[4] = rol32(ws[4] ^ ws[9], 7);
+ }
+
+ for (i = 0; i < CHACHA20_BLOCK_SIZE_WORDS; i++)
+ out[i] = le_bswap32(ws[i] + state[i]);
+
+ state[12]++;
+}
+
+static inline int drng_chacha20_selftest_one(struct chacha20_state *state,
+ uint32_t *expected)
+{
+ uint32_t result[CHACHA20_BLOCK_SIZE_WORDS];
+
+ chacha20_block(&state->constants[0], result);
+ return memcmp(expected, result, CHACHA20_BLOCK_SIZE);
+}
+
+static int drng_chacha20_selftest(void)
+{
+ struct chacha20_state chacha20;
+ uint32_t expected[CHACHA20_BLOCK_SIZE_WORDS];
+
+ /* Test vector according to RFC 7539 section 2.3.2 */
+ chacha20.constants[0] = 0x61707865; chacha20.constants[1] = 0x3320646e;
+ chacha20.constants[2] = 0x79622d32; chacha20.constants[3] = 0x6b206574;
+ chacha20.key.u[0] = 0x03020100; chacha20.key.u[1] = 0x07060504;
+ chacha20.key.u[2] = 0x0b0a0908; chacha20.key.u[3] = 0x0f0e0d0c;
+ chacha20.key.u[4] = 0x13121110; chacha20.key.u[5] = 0x17161514;
+ chacha20.key.u[6] = 0x1b1a1918; chacha20.key.u[7] = 0x1f1e1d1c;
+ chacha20.counter = 0x00000001; chacha20.nonce[0] = 0x09000000;
+ chacha20.nonce[1] = 0x4a000000; chacha20.nonce[2] = 0x00000000;
+
+ expected[0] = 0xe4e7f110; expected[1] = 0x15593bd1;
+ expected[2] = 0x1fdd0f50; expected[3] = 0xc47120a3;
+ expected[4] = 0xc7f4d1c7; expected[5] = 0x0368c033;
+ expected[6] = 0x9aaa2204; expected[7] = 0x4e6cd4c3;
+ expected[8] = 0x466482d2; expected[9] = 0x09aa9f07;
+ expected[10] = 0x05d7c214; expected[11] = 0xa2028bd9;
+ expected[12] = 0xd19c12b5; expected[13] = 0xb94e16de;
+ expected[14] = 0xe883d0cb; expected[15] = 0x4e3c50a2;
+
+ return drng_chacha20_selftest_one(&chacha20, &expected[0]);
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+
+static int drng_random_get(uint8_t *buf, uint32_t buflen)
+{
+ int ret = randombytes(buf, buflen);
+ if( ret != 0) {
+ return 0;
+ } else {
+ return buflen;
+ }
+}
+
+/******************************* ChaCha20 DRNG *******************************/
+
+struct chacha20_drng {
+ struct chacha20_state chacha20;
+ time_t last_seeded;
+ uint64_t generated_bytes;
+};
+
+/**
+ * Update of the ChaCha20 state by generating one ChaCha20 block which is
+ * equal to the state of the ChaCha20. The generated block is XORed into
+ * the key part of the state. This shall ensure backtracking resistance as well
+ * as a proper mix of the ChaCha20 state once the key is injected.
+ */
+static inline void drng_chacha20_update(struct chacha20_state *chacha20,
+ uint32_t *buf, uint32_t used_words)
+{
+ uint32_t i, tmp[CHACHA20_BLOCK_SIZE_WORDS];
+
+ if (used_words > CHACHA20_KEY_SIZE_WORDS) {
+ chacha20_block(&chacha20->constants[0], tmp);
+ for (i = 0; i < CHACHA20_KEY_SIZE_WORDS; i++)
+ chacha20->key.u[i] ^= tmp[i];
+ memset_secure(tmp, 0, sizeof(tmp));
+ } else {
+ for (i = 0; i < CHACHA20_KEY_SIZE_WORDS; i++)
+ chacha20->key.u[i] ^= buf[i + used_words];
+ }
+
+ /* Deterministic increment of nonce as required in RFC 7539 chapter 4 */
+ chacha20->nonce[0]++;
+ if (chacha20->nonce[0] == 0)
+ chacha20->nonce[1]++;
+ if (chacha20->nonce[1] == 0)
+ chacha20->nonce[2]++;
+
+ /* Leave counter untouched as it is start value is undefined in RFC */
+}
+
+/**
+ * Seed the ChaCha20 DRNG by injecting the input data into the key part of
+ * the ChaCha20 state. If the input data is longer than the ChaCha20 key size,
+ * perform a ChaCha20 operation after processing of key size input data.
+ * This operation shall spread out the entropy into the ChaCha20 state before
+ * new entropy is injected into the key part.
+ *
+ * The approach taken here is logically similar to a CBC-MAC: The input data
+ * is processed chunk-wise. Each chunk is encrypted, the output is XORed with
+ * the next chunk of the input and then encrypted again. I.e. the
+ * ChaCha20 CBC-MAC of the seed data is injected into the DRNG state.
+ */
+static int drng_chacha20_seed(struct chacha20_state *chacha20,
+ const uint8_t *inbuf, uint32_t inbuflen)
+{
+ while (inbuflen) {
+ uint32_t i, todo = min(inbuflen, CHACHA20_KEY_SIZE);
+
+ for (i = 0; i < todo; i++)
+ chacha20->key.b[i] ^= inbuf[i];
+
+ /* Break potential dependencies between the inbuf key blocks */
+ drng_chacha20_update(chacha20, NULL, CHACHA20_BLOCK_SIZE_WORDS);
+ inbuf += todo;
+ inbuflen -= todo;
+ }
+
+ return 0;
+}
+
+/**
+ * Chacha20 DRNG generation of random numbers: the stream output of ChaCha20
+ * is the random number. After the completion of the generation of the
+ * stream, the entire ChaCha20 state is updated.
+ *
+ * Note, as the ChaCha20 implements a 32 bit counter, we must ensure
+ * that this function is only invoked for at most 2^32 - 1 ChaCha20 blocks
+ * before a reseed or an update happens. This is ensured by the variable
+ * outbuflen which is a 32 bit integer defining the number of bytes to be
+ * generated by the ChaCha20 DRNG. At the end of this function, an update
+ * operation is invoked which implies that the 32 bit counter will never be
+ * overflown in this implementation.
+ */
+static int drng_chacha20_generate(struct chacha20_state *chacha20,
+ uint8_t *outbuf, uint32_t outbuflen)
+{
+ uint32_t aligned_buf[(CHACHA20_BLOCK_SIZE / sizeof(uint32_t))];
+ uint32_t used = CHACHA20_BLOCK_SIZE_WORDS;
+ int zeroize_buf = 0;
+
+ while (outbuflen >= CHACHA20_BLOCK_SIZE) {
+ if ((uintptr_t)outbuf & (sizeof(aligned_buf[0]) - 1)) {
+ chacha20_block(&chacha20->constants[0], aligned_buf);
+ memcpy(outbuf, aligned_buf, CHACHA20_BLOCK_SIZE);
+ zeroize_buf = 1;
+ } else {
+ chacha20_block(&chacha20->constants[0],
+ (uint32_t *)outbuf);
+ }
+
+ outbuf += CHACHA20_BLOCK_SIZE;
+ outbuflen -= CHACHA20_BLOCK_SIZE;
+ }
+
+ if (outbuflen) {
+ chacha20_block(&chacha20->constants[0], aligned_buf);
+ memcpy(outbuf, aligned_buf, outbuflen);
+ used = ((outbuflen + sizeof(aligned_buf[0]) - 1) /
+ sizeof(aligned_buf[0]));
+ zeroize_buf = 1;
+ }
+
+ drng_chacha20_update(chacha20, aligned_buf, used);
+
+ if (zeroize_buf)
+ memset_secure(aligned_buf, 0, sizeof(aligned_buf));
+
+ return 0;
+}
+
+static int drng_chacha20_rng_selftest(struct chacha20_drng *drng)
+{
+ int ret;
+ uint8_t outbuf[CHACHA20_KEY_SIZE * 2] __aligned(sizeof(uint32_t));
+ uint8_t seed[CHACHA20_KEY_SIZE * 2] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ };
+
+ /*
+ * Expected result when ChaCha20 DRNG state is zero:
+ * * constants are set to "expand 32-byte k"
+ * * remaining state is 0
+ * and pulling one ChaCha20 DRNG block.
+ */
+ uint8_t expected_block[CHACHA20_KEY_SIZE] = {
+ 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
+ 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
+ 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
+ 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7 };
+
+ /*
+ * Expected result when ChaCha20 DRNG state is zero:
+ * * constants are set to "expand 32-byte k"
+ * * remaining state is 0
+ * followed by a reseed with
+ * 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ * 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ * 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ * 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ * 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ * 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ * 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ * 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
+ * and pulling two ChaCha20 DRNG blocks.
+ */
+ uint8_t expected_twoblocks[CHACHA20_KEY_SIZE * 2] = {
+ 0xf5, 0xb4, 0xb6, 0x5a, 0xec, 0xcd, 0x5a, 0x65,
+ 0x87, 0x56, 0xe3, 0x86, 0x51, 0x54, 0xfc, 0x90,
+ 0x56, 0xff, 0x5e, 0xae, 0x58, 0xf2, 0x01, 0x88,
+ 0xb1, 0x7e, 0xb8, 0x2e, 0x17, 0x9a, 0x27, 0xe6,
+ 0x86, 0xb3, 0xed, 0x33, 0xf7, 0xb9, 0x06, 0x05,
+ 0x8a, 0x2d, 0x1a, 0x93, 0xc9, 0x0b, 0x80, 0x04,
+ 0x03, 0xaa, 0x60, 0xaf, 0xd5, 0x36, 0x40, 0x11,
+ 0x67, 0x89, 0xb1, 0x66, 0xd5, 0x88, 0x62, 0x6d };
+
+ /*
+ * Expected result when ChaCha20 DRNG state is zero:
+ * * constants are set to "expand 32-byte k"
+ * * remaining state is 0
+ * followed by a reseed with
+ * 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ * 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ * 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ * 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ * 0x20
+ * and pulling one ChaCha20 DRNG block plus one byte.
+ */
+ uint8_t expected_block_and_byte[CHACHA20_KEY_SIZE + 1] = {
+ 0x3d, 0x13, 0x47, 0x1e, 0x7f, 0x7c, 0x99, 0x33,
+ 0xfc, 0x44, 0xa4, 0xdd, 0xf9, 0x3d, 0xe1, 0x9a,
+ 0xd4, 0xe8, 0x7a, 0x7d, 0x42, 0xac, 0xd1, 0xcd,
+ 0x10, 0x69, 0xe7, 0xbf, 0xd4, 0xfd, 0x69, 0x4b,
+ 0xa7 };
+
+ /* Generate with zero state */
+ ret = drng_chacha20_generate(&drng->chacha20, outbuf,
+ sizeof(expected_block));
+ if (ret)
+ return ret;
+ if (memcmp(outbuf, expected_block, sizeof(expected_block)))
+ return -EFAULT;
+
+ /* Clear state of DRNG */
+ memset(&drng->chacha20.key.u[0], 0, 48);
+
+ /* Reseed with 2 blocks */
+ ret = drng_chacha20_seed(&drng->chacha20, seed,
+ sizeof(expected_twoblocks));
+ if (ret)
+ return ret;
+ ret = drng_chacha20_generate(&drng->chacha20, outbuf,
+ sizeof(expected_twoblocks));
+ if (ret)
+ return ret;
+ if (memcmp(outbuf, expected_twoblocks, sizeof(expected_twoblocks)))
+ return -EFAULT;
+
+ /* Clear state of DRNG */
+ memset(&drng->chacha20.key.u[0], 0, 48);
+
+ /* Reseed with 1 block and one byte */
+ ret = drng_chacha20_seed(&drng->chacha20, seed,
+ sizeof(expected_block_and_byte));
+ if (ret)
+ return ret;
+ ret = drng_chacha20_generate(&drng->chacha20, outbuf,
+ sizeof(expected_block_and_byte));
+ if (ret)
+ return ret;
+ if (memcmp(outbuf, expected_block_and_byte,
+ sizeof(expected_block_and_byte)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static void drng_chacha20_dealloc(struct chacha20_drng *drng)
+{
+ memset_secure(drng, 0, sizeof(*drng));
+#ifdef _WIN32
+ _aligned_free(drng);
+#else
+ free(drng);
+#endif
+}
+
+/**
+ * Allocation of the DRBG state
+ */
+static int drng_chacha20_alloc(struct chacha20_drng **out)
+{
+ struct chacha20_drng *drng;
+ uint32_t i, v = 0;
+ int ret = 0;
+
+ if (drng_chacha20_selftest()) {
+ return -EFAULT;
+ }
+
+#ifdef _WIN32
+ drng = _aligned_malloc(sizeof(*drng), CHACHA20_DRNG_ALIGNMENT);
+#endif
+
+#ifndef aligned_alloc
+ drng = malloc(sizeof(*drng));
+#else
+ drng = aligned_alloc(CHACHA20_DRNG_ALIGNMENT, sizeof(*drng));
+#endif
+ if (drng == NULL) {
+ return -1;
+ }
+
+#ifndef _WIN32
+ /* prevent paging out of the memory state to swap space */
+ ret = mlock(drng, sizeof(*drng));
+ if (ret && errno != EPERM && errno != EAGAIN) {
+ ret = -errno;
+ goto err;
+ }
+#endif
+
+ memset(drng, 0, sizeof(*drng));
+
+ memcpy(&drng->chacha20.constants[0], "expand 32-byte k", 16);
+
+ ret = drng_chacha20_rng_selftest(drng);
+ if (ret)
+ goto err;
+
+ /* Update the state left by the self test */
+ for (i = 0; i < CHACHA20_KEY_SIZE_WORDS; i++) {
+ get_time(NULL, &v);
+ drng->chacha20.key.u[i] ^= v;
+ }
+
+ for (i = 0; i < 3; i++) {
+ get_time(NULL, &v);
+ drng->chacha20.nonce[i] ^= v;
+ }
+
+ *out = drng;
+
+ return 0;
+
+err:
+ drng_chacha20_dealloc(drng);
+ return ret;
+}
+
+/***************************** ChaCha20 DRNG API *****************************/
+
+DSO_PUBLIC
+int drng_chacha20_reseed(struct chacha20_drng *drng, const uint8_t *inbuf,
+ uint32_t inbuflen)
+{
+ uint8_t seed[CHACHA20_KEY_SIZE * 2];
+ int ret;
+ uint32_t collected = 0;
+
+ /* Entropy assumption: 1 data bit delivers one bit of entropy */
+ ret = drng_random_get(seed, CHACHA20_KEY_SIZE);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ collected += ret;
+
+ ret = drng_chacha20_seed(&drng->chacha20, seed,
+ CHACHA20_KEY_SIZE);
+ if (ret)
+ return ret;
+ }
+
+ memset_secure(seed, 0, sizeof(seed));
+
+ /* Internal noise sources must have delivered sufficient information */
+ if (collected < CHACHA20_KEY_SIZE)
+ return -EFAULT;
+
+ if (inbuf && inbuflen)
+ ret = drng_chacha20_seed(&drng->chacha20, inbuf, inbuflen);
+
+ get_time(&drng->last_seeded, NULL);
+ drng->generated_bytes = 0;
+
+ return ret;
+}
+
+DSO_PUBLIC
+void drng_chacha20_destroy(struct chacha20_drng *drng)
+{
+ drng_chacha20_dealloc(drng);
+}
+
+DSO_PUBLIC
+int drng_chacha20_init(struct chacha20_drng **drng)
+{
+ int ret = drng_chacha20_alloc(drng);
+
+ if (ret)
+ return ret;
+
+ ret = drng_chacha20_reseed(*drng, NULL, 0);
+ if (ret) {
+ drng_chacha20_destroy(*drng);
+ return ret;
+ }
+
+ return 0;
+}
+
+DSO_PUBLIC
+int drng_chacha20_get(struct chacha20_drng *drng, uint8_t *outbuf,
+ uint32_t outbuflen)
+{
+ time_t now = 0;
+ uint32_t nsec;
+ int ret;
+
+ get_time(&now, &nsec);
+
+ /*
+ * Reseed if:
+ * * last seeding was more than 600 seconds ago
+ * * more than 1<<30 bytes were generated since last reseed
+ */
+ if (((now - drng->last_seeded) > 600) ||
+ (drng->generated_bytes > (1<<30))) {
+ ret = drng_chacha20_reseed(drng, (uint8_t *)&nsec,
+ sizeof(nsec));
+
+ if (ret)
+ return ret;
+ drng->last_seeded = now;
+ drng->generated_bytes = 0;
+ } else {
+ ret = drng_chacha20_seed(&drng->chacha20, (uint8_t *)&nsec,
+ sizeof(nsec));
+ if (ret)
+ return ret;
+ }
+
+ ret = drng_chacha20_generate(&drng->chacha20, outbuf, outbuflen);
+ if (ret)
+ return ret;
+
+ drng->generated_bytes += outbuflen;
+
+ return 0;
+}
+
+DSO_PUBLIC
+int drng_chacha20_versionstring(char *buf, size_t buflen)
+{
+ return snprintf(buf, buflen, "ChaCha20 DRNG %d.%d.%d",
+ MAJVERSION, MINVERSION, PATCHLEVEL);
+}
+
+DSO_PUBLIC
+uint32_t drng_chacha20_version(void)
+{
+ uint32_t version = 0;
+
+ version = MAJVERSION * 1000000;
+ version += MINVERSION * 10000;
+ version += PATCHLEVEL * 100;
+
+ return version;
+}
diff --git a/src/chacha20_drng.h b/src/chacha20_drng.h
new file mode 100644
index 0000000..f91966b
--- /dev/null
+++ b/src/chacha20_drng.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 - 2017, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _CHACHA20_DRNG_H
+#define _CHACHA20_DRNG_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct chacha20_drng;
+
+/**
+ * DOC: ChaCha20 DRNG API
+ *
+ * API function calls used to invoke ChaCha20 DRNG.
+ */
+
+/**
+ * drng_chacha20_init() - Initialization of a ChaCha20 DRNG cipher handle
+ *
+ * @drng: [out] cipher handle allocated by the function
+ *
+ * The cipher handle including its memory is allocated with this function.
+ *
+ * Before the allocation is performed, a self test regarding the correct
+ * operation of the ChaCha20 cipher is performed. Only when the self test
+ * succeeds, the allocation operation is performed.
+ *
+ * The memory is pinned so that the DRNG state cannot be swapped out to disk.
+ *
+ * As part of the allocation, the seed source is initialized.
+ *
+ * The state of the DRNG is automatically seeded from the internal
+ * noise source. Thus, the caller may immediately generate random numbers
+ * without providing (additional) seed.
+ *
+ * @return 0 upon success; < 0 on error
+ */
+int drng_chacha20_init(struct chacha20_drng **drng);
+
+/**
+ * drng_chacha20_destroy() - Secure deletion of the ChaCha20 DRNG cipher handle
+ *
+ * @drng: [in] cipher handle to be deallocated
+ *
+ * During the deallocation operation, the seed source is properly
+ * disposed of.
+ *
+ * Also, the used memory is securely erased.
+ *
+ */
+void drng_chacha20_destroy(struct chacha20_drng *drng);
+
+/**
+ * drng_chacha20_get() - Obtain random numbers
+ *
+ * @drng: [in] allocated ChaCha20 cipher handle
+ * @outbuf: [out] allocated buffer that is to be filled with random numbers
+ * @outbuflen: [in] length of outbuf indicating the size of the random
+ * number byte string to be generated
+ *
+ * Generate random numbers and fill the buffer provided by the caller.
+ *
+ * Before each request of random numbers, a high-resolution time stamp is
+ * mixed into the random number generator state.
+ *
+ * If the last (re)seeding operation is longer than 600 seconds ago or
+ * more than 1GB of random numbers were generated, an automated
+ * reseed is performed.
+ *
+ * After the generation of random numbers, the internal state of the ChaCha20
+ * DRNG is completely re-created using ChaCha20 to provide enhanced backtracking
+ * resistance. I.e. if the state of the DRNG becomes known after generation
+ * of random numbers, an attacker cannot deduce the already generated
+ * random numbers.
+ *
+ * @return 0 upon success; < 0 on error
+ */
+int drng_chacha20_get(struct chacha20_drng *drng, uint8_t *outbuf,
+ uint32_t outbuflen);
+
+/**
+ * drng_chacha20_reseed() - Reseed the ChaCha20 DRNG
+ *
+ * @drng: [in] allocated ChaCha20 cipher handle
+ * @inbuf: [in] buffer with the seed data
+ * @inbuflen: [in] length of inbuf
+ *
+ * When calling the function, the DRNG is first seeded from its internal
+ * noise sources. This is followed by seeding the DRNG with the caller-provided
+ * data.
+ *
+ * @return 0 upon succes; < 0 on error
+ */
+int drng_chacha20_reseed(struct chacha20_drng *drng, const uint8_t *inbuf,
+ uint32_t inbuflen);
+
+/**
+ * drng_chacha20_versionstring() - obtain version string of ChaCha20 DRNG
+ *
+ * @buf: [out] buffer to place version string into
+ * @buflen: [in] length of buffer
+ *
+ * @buf is filled with a string of the form "chacha20 DRNG X.Y.Z". Care
+ * should be taken to provide a large enough buffer taking in to account that
+ * X, Y, and/or Z may be multiple characters.
+ *
+ * @return status of snprintf(3). < 0 on error, otherwise length of written
+ * string. See snprintf(3) for details on truncation.
+ */
+int drng_chacha20_versionstring(char *buf, size_t buflen);
+
+/**
+ * drng_chacha20_version() - return machine-usable version number of ChaCha20
+ * DRNG
+ *
+ * The function returns a version number that is monotonic increasing
+ * for newer versions. The version numbers are multiples of 100. For example,
+ * version 1.2.3 is converted to 1020300 -- the last two digits are reserved
+ * for future use.
+ *
+ * The result of this function can be used in comparing the version number
+ * in a calling program if version-specific calls need to be make.
+ *
+ * @return Version number of ChaCha20 DRNG
+ */
+uint32_t drng_chacha20_version(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CHACHA20_DRNG_H */
diff --git a/src/curve25519-donna-32bit.h b/src/curve25519-donna-32bit.h
new file mode 100644
index 0000000..5ef91a2
--- /dev/null
+++ b/src/curve25519-donna-32bit.h
@@ -0,0 +1,466 @@
+typedef uint32_t bignum25519[10];
+
+static const uint32_t reduce_mask_26 = (1 << 26) - 1;
+static const uint32_t reduce_mask_25 = (1 << 25) - 1;
+
+/* out = in */
+DONNA_INLINE static void
+curve25519_copy(bignum25519 out, const bignum25519 in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+ out[5] = in[5];
+ out[6] = in[6];
+ out[7] = in[7];
+ out[8] = in[8];
+ out[9] = in[9];
+}
+
+/* out = a + b */
+DONNA_INLINE static void
+curve25519_add(bignum25519 out, const bignum25519 a, const bignum25519 b) {
+ out[0] = a[0] + b[0];
+ out[1] = a[1] + b[1];
+ out[2] = a[2] + b[2];
+ out[3] = a[3] + b[3];
+ out[4] = a[4] + b[4];
+ out[5] = a[5] + b[5];
+ out[6] = a[6] + b[6];
+ out[7] = a[7] + b[7];
+ out[8] = a[8] + b[8];
+ out[9] = a[9] + b[9];
+}
+
+/* out = a - b */
+DONNA_INLINE static void
+curve25519_sub(bignum25519 out, const bignum25519 a, const bignum25519 b) {
+ uint32_t c;
+ out[0] = 0x7ffffda + a[0] - b[0] ; c = (out[0] >> 26); out[0] &= reduce_mask_26;
+ out[1] = 0x3fffffe + a[1] - b[1] + c; c = (out[1] >> 25); out[1] &= reduce_mask_25;
+ out[2] = 0x7fffffe + a[2] - b[2] + c; c = (out[2] >> 26); out[2] &= reduce_mask_26;
+ out[3] = 0x3fffffe + a[3] - b[3] + c; c = (out[3] >> 25); out[3] &= reduce_mask_25;
+ out[4] = 0x7fffffe + a[4] - b[4] + c; c = (out[4] >> 26); out[4] &= reduce_mask_26;
+ out[5] = 0x3fffffe + a[5] - b[5] + c; c = (out[5] >> 25); out[5] &= reduce_mask_25;
+ out[6] = 0x7fffffe + a[6] - b[6] + c; c = (out[6] >> 26); out[6] &= reduce_mask_26;
+ out[7] = 0x3fffffe + a[7] - b[7] + c; c = (out[7] >> 25); out[7] &= reduce_mask_25;
+ out[8] = 0x7fffffe + a[8] - b[8] + c; c = (out[8] >> 26); out[8] &= reduce_mask_26;
+ out[9] = 0x3fffffe + a[9] - b[9] + c; c = (out[9] >> 25); out[9] &= reduce_mask_25;
+ out[0] += 19 * c;
+}
+
+/* out = in * scalar */
+DONNA_INLINE static void
+curve25519_scalar_product(bignum25519 out, const bignum25519 in, const uint32_t scalar) {
+ uint64_t a;
+ uint32_t c;
+ a = mul32x32_64(in[0], scalar); out[0] = (uint32_t)a & reduce_mask_26; c = (uint32_t)(a >> 26);
+ a = mul32x32_64(in[1], scalar) + c; out[1] = (uint32_t)a & reduce_mask_25; c = (uint32_t)(a >> 25);
+ a = mul32x32_64(in[2], scalar) + c; out[2] = (uint32_t)a & reduce_mask_26; c = (uint32_t)(a >> 26);
+ a = mul32x32_64(in[3], scalar) + c; out[3] = (uint32_t)a & reduce_mask_25; c = (uint32_t)(a >> 25);
+ a = mul32x32_64(in[4], scalar) + c; out[4] = (uint32_t)a & reduce_mask_26; c = (uint32_t)(a >> 26);
+ a = mul32x32_64(in[5], scalar) + c; out[5] = (uint32_t)a & reduce_mask_25; c = (uint32_t)(a >> 25);
+ a = mul32x32_64(in[6], scalar) + c; out[6] = (uint32_t)a & reduce_mask_26; c = (uint32_t)(a >> 26);
+ a = mul32x32_64(in[7], scalar) + c; out[7] = (uint32_t)a & reduce_mask_25; c = (uint32_t)(a >> 25);
+ a = mul32x32_64(in[8], scalar) + c; out[8] = (uint32_t)a & reduce_mask_26; c = (uint32_t)(a >> 26);
+ a = mul32x32_64(in[9], scalar) + c; out[9] = (uint32_t)a & reduce_mask_25; c = (uint32_t)(a >> 25);
+ out[0] += c * 19;
+}
+
+/* out = a * b */
+DONNA_INLINE static void
+curve25519_mul(bignum25519 out, const bignum25519 a, const bignum25519 b) {
+ uint32_t r0,r1,r2,r3,r4,r5,r6,r7,r8,r9;
+ uint32_t s0,s1,s2,s3,s4,s5,s6,s7,s8,s9;
+ uint64_t m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,c;
+ uint32_t p;
+
+ r0 = b[0];
+ r1 = b[1];
+ r2 = b[2];
+ r3 = b[3];
+ r4 = b[4];
+ r5 = b[5];
+ r6 = b[6];
+ r7 = b[7];
+ r8 = b[8];
+ r9 = b[9];
+
+ s0 = a[0];
+ s1 = a[1];
+ s2 = a[2];
+ s3 = a[3];
+ s4 = a[4];
+ s5 = a[5];
+ s6 = a[6];
+ s7 = a[7];
+ s8 = a[8];
+ s9 = a[9];
+
+ m1 = mul32x32_64(r0, s1) + mul32x32_64(r1, s0);
+ m3 = mul32x32_64(r0, s3) + mul32x32_64(r1, s2) + mul32x32_64(r2, s1) + mul32x32_64(r3, s0);
+ m5 = mul32x32_64(r0, s5) + mul32x32_64(r1, s4) + mul32x32_64(r2, s3) + mul32x32_64(r3, s2) + mul32x32_64(r4, s1) + mul32x32_64(r5, s0);
+ m7 = mul32x32_64(r0, s7) + mul32x32_64(r1, s6) + mul32x32_64(r2, s5) + mul32x32_64(r3, s4) + mul32x32_64(r4, s3) + mul32x32_64(r5, s2) + mul32x32_64(r6, s1) + mul32x32_64(r7, s0);
+ m9 = mul32x32_64(r0, s9) + mul32x32_64(r1, s8) + mul32x32_64(r2, s7) + mul32x32_64(r3, s6) + mul32x32_64(r4, s5) + mul32x32_64(r5, s4) + mul32x32_64(r6, s3) + mul32x32_64(r7, s2) + mul32x32_64(r8, s1) + mul32x32_64(r9, s0);
+
+ r1 *= 2;
+ r3 *= 2;
+ r5 *= 2;
+ r7 *= 2;
+
+ m0 = mul32x32_64(r0, s0);
+ m2 = mul32x32_64(r0, s2) + mul32x32_64(r1, s1) + mul32x32_64(r2, s0);
+ m4 = mul32x32_64(r0, s4) + mul32x32_64(r1, s3) + mul32x32_64(r2, s2) + mul32x32_64(r3, s1) + mul32x32_64(r4, s0);
+ m6 = mul32x32_64(r0, s6) + mul32x32_64(r1, s5) + mul32x32_64(r2, s4) + mul32x32_64(r3, s3) + mul32x32_64(r4, s2) + mul32x32_64(r5, s1) + mul32x32_64(r6, s0);
+ m8 = mul32x32_64(r0, s8) + mul32x32_64(r1, s7) + mul32x32_64(r2, s6) + mul32x32_64(r3, s5) + mul32x32_64(r4, s4) + mul32x32_64(r5, s3) + mul32x32_64(r6, s2) + mul32x32_64(r7, s1) + mul32x32_64(r8, s0);
+
+ r1 *= 19;
+ r2 *= 19;
+ r3 = (r3 / 2) * 19;
+ r4 *= 19;
+ r5 = (r5 / 2) * 19;
+ r6 *= 19;
+ r7 = (r7 / 2) * 19;
+ r8 *= 19;
+ r9 *= 19;
+
+ m1 += (mul32x32_64(r9, s2) + mul32x32_64(r8, s3) + mul32x32_64(r7, s4) + mul32x32_64(r6, s5) + mul32x32_64(r5, s6) + mul32x32_64(r4, s7) + mul32x32_64(r3, s8) + mul32x32_64(r2, s9));
+ m3 += (mul32x32_64(r9, s4) + mul32x32_64(r8, s5) + mul32x32_64(r7, s6) + mul32x32_64(r6, s7) + mul32x32_64(r5, s8) + mul32x32_64(r4, s9));
+ m5 += (mul32x32_64(r9, s6) + mul32x32_64(r8, s7) + mul32x32_64(r7, s8) + mul32x32_64(r6, s9));
+ m7 += (mul32x32_64(r9, s8) + mul32x32_64(r8, s9));
+
+ r3 *= 2;
+ r5 *= 2;
+ r7 *= 2;
+ r9 *= 2;
+
+ m0 += (mul32x32_64(r9, s1) + mul32x32_64(r8, s2) + mul32x32_64(r7, s3) + mul32x32_64(r6, s4) + mul32x32_64(r5, s5) + mul32x32_64(r4, s6) + mul32x32_64(r3, s7) + mul32x32_64(r2, s8) + mul32x32_64(r1, s9));
+ m2 += (mul32x32_64(r9, s3) + mul32x32_64(r8, s4) + mul32x32_64(r7, s5) + mul32x32_64(r6, s6) + mul32x32_64(r5, s7) + mul32x32_64(r4, s8) + mul32x32_64(r3, s9));
+ m4 += (mul32x32_64(r9, s5) + mul32x32_64(r8, s6) + mul32x32_64(r7, s7) + mul32x32_64(r6, s8) + mul32x32_64(r5, s9));
+ m6 += (mul32x32_64(r9, s7) + mul32x32_64(r8, s8) + mul32x32_64(r7, s9));
+ m8 += (mul32x32_64(r9, s9));
+
+ r0 = (uint32_t)m0 & reduce_mask_26; c = (m0 >> 26);
+ m1 += c; r1 = (uint32_t)m1 & reduce_mask_25; c = (m1 >> 25);
+ m2 += c; r2 = (uint32_t)m2 & reduce_mask_26; c = (m2 >> 26);
+ m3 += c; r3 = (uint32_t)m3 & reduce_mask_25; c = (m3 >> 25);
+ m4 += c; r4 = (uint32_t)m4 & reduce_mask_26; c = (m4 >> 26);
+ m5 += c; r5 = (uint32_t)m5 & reduce_mask_25; c = (m5 >> 25);
+ m6 += c; r6 = (uint32_t)m6 & reduce_mask_26; c = (m6 >> 26);
+ m7 += c; r7 = (uint32_t)m7 & reduce_mask_25; c = (m7 >> 25);
+ m8 += c; r8 = (uint32_t)m8 & reduce_mask_26; c = (m8 >> 26);
+ m9 += c; r9 = (uint32_t)m9 & reduce_mask_25; p = (uint32_t)(m9 >> 25);
+ m0 = r0 + mul32x32_64(p,19); r0 = (uint32_t)m0 & reduce_mask_26; p = (uint32_t)(m0 >> 26);
+ r1 += p;
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+ out[5] = r5;
+ out[6] = r6;
+ out[7] = r7;
+ out[8] = r8;
+ out[9] = r9;
+}
+
+/* out = in * in */
+DONNA_INLINE static void
+curve25519_square(bignum25519 out, const bignum25519 in) {
+ uint32_t r0,r1,r2,r3,r4,r5,r6,r7,r8,r9;
+ uint32_t d6,d7,d8,d9;
+ uint64_t m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,c;
+ uint32_t p;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+ r5 = in[5];
+ r6 = in[6];
+ r7 = in[7];
+ r8 = in[8];
+ r9 = in[9];
+
+
+ m0 = mul32x32_64(r0, r0);
+ r0 *= 2;
+ m1 = mul32x32_64(r0, r1);
+ m2 = mul32x32_64(r0, r2) + mul32x32_64(r1, r1 * 2);
+ r1 *= 2;
+ m3 = mul32x32_64(r0, r3) + mul32x32_64(r1, r2 );
+ m4 = mul32x32_64(r0, r4) + mul32x32_64(r1, r3 * 2) + mul32x32_64(r2, r2);
+ r2 *= 2;
+ m5 = mul32x32_64(r0, r5) + mul32x32_64(r1, r4 ) + mul32x32_64(r2, r3);
+ m6 = mul32x32_64(r0, r6) + mul32x32_64(r1, r5 * 2) + mul32x32_64(r2, r4) + mul32x32_64(r3, r3 * 2);
+ r3 *= 2;
+ m7 = mul32x32_64(r0, r7) + mul32x32_64(r1, r6 ) + mul32x32_64(r2, r5) + mul32x32_64(r3, r4 );
+ m8 = mul32x32_64(r0, r8) + mul32x32_64(r1, r7 * 2) + mul32x32_64(r2, r6) + mul32x32_64(r3, r5 * 2) + mul32x32_64(r4, r4 );
+ m9 = mul32x32_64(r0, r9) + mul32x32_64(r1, r8 ) + mul32x32_64(r2, r7) + mul32x32_64(r3, r6 ) + mul32x32_64(r4, r5 * 2);
+
+ d6 = r6 * 19;
+ d7 = r7 * 2 * 19;
+ d8 = r8 * 19;
+ d9 = r9 * 2 * 19;
+
+ m0 += (mul32x32_64(d9, r1 ) + mul32x32_64(d8, r2 ) + mul32x32_64(d7, r3 ) + mul32x32_64(d6, r4 * 2) + mul32x32_64(r5, r5 * 2 * 19));
+ m1 += (mul32x32_64(d9, r2 / 2) + mul32x32_64(d8, r3 ) + mul32x32_64(d7, r4 ) + mul32x32_64(d6, r5 * 2));
+ m2 += (mul32x32_64(d9, r3 ) + mul32x32_64(d8, r4 * 2) + mul32x32_64(d7, r5 * 2) + mul32x32_64(d6, r6 ));
+ m3 += (mul32x32_64(d9, r4 ) + mul32x32_64(d8, r5 * 2) + mul32x32_64(d7, r6 ));
+ m4 += (mul32x32_64(d9, r5 * 2) + mul32x32_64(d8, r6 * 2) + mul32x32_64(d7, r7 ));
+ m5 += (mul32x32_64(d9, r6 ) + mul32x32_64(d8, r7 * 2));
+ m6 += (mul32x32_64(d9, r7 * 2) + mul32x32_64(d8, r8 ));
+ m7 += (mul32x32_64(d9, r8 ));
+ m8 += (mul32x32_64(d9, r9 ));
+
+ r0 = (uint32_t)m0 & reduce_mask_26; c = (m0 >> 26);
+ m1 += c; r1 = (uint32_t)m1 & reduce_mask_25; c = (m1 >> 25);
+ m2 += c; r2 = (uint32_t)m2 & reduce_mask_26; c = (m2 >> 26);
+ m3 += c; r3 = (uint32_t)m3 & reduce_mask_25; c = (m3 >> 25);
+ m4 += c; r4 = (uint32_t)m4 & reduce_mask_26; c = (m4 >> 26);
+ m5 += c; r5 = (uint32_t)m5 & reduce_mask_25; c = (m5 >> 25);
+ m6 += c; r6 = (uint32_t)m6 & reduce_mask_26; c = (m6 >> 26);
+ m7 += c; r7 = (uint32_t)m7 & reduce_mask_25; c = (m7 >> 25);
+ m8 += c; r8 = (uint32_t)m8 & reduce_mask_26; c = (m8 >> 26);
+ m9 += c; r9 = (uint32_t)m9 & reduce_mask_25; p = (uint32_t)(m9 >> 25);
+ m0 = r0 + mul32x32_64(p,19); r0 = (uint32_t)m0 & reduce_mask_26; p = (uint32_t)(m0 >> 26);
+ r1 += p;
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+ out[5] = r5;
+ out[6] = r6;
+ out[7] = r7;
+ out[8] = r8;
+ out[9] = r9;
+}
+
+/* out = in^(2 * count) */
+static void
+curve25519_square_times(bignum25519 out, const bignum25519 in, int count) {
+ uint32_t r0,r1,r2,r3,r4,r5,r6,r7,r8,r9;
+ uint32_t d6,d7,d8,d9;
+ uint64_t m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,c;
+ uint32_t p;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+ r5 = in[5];
+ r6 = in[6];
+ r7 = in[7];
+ r8 = in[8];
+ r9 = in[9];
+
+ do {
+ m0 = mul32x32_64(r0, r0);
+ r0 *= 2;
+ m1 = mul32x32_64(r0, r1);
+ m2 = mul32x32_64(r0, r2) + mul32x32_64(r1, r1 * 2);
+ r1 *= 2;
+ m3 = mul32x32_64(r0, r3) + mul32x32_64(r1, r2 );
+ m4 = mul32x32_64(r0, r4) + mul32x32_64(r1, r3 * 2) + mul32x32_64(r2, r2);
+ r2 *= 2;
+ m5 = mul32x32_64(r0, r5) + mul32x32_64(r1, r4 ) + mul32x32_64(r2, r3);
+ m6 = mul32x32_64(r0, r6) + mul32x32_64(r1, r5 * 2) + mul32x32_64(r2, r4) + mul32x32_64(r3, r3 * 2);
+ r3 *= 2;
+ m7 = mul32x32_64(r0, r7) + mul32x32_64(r1, r6 ) + mul32x32_64(r2, r5) + mul32x32_64(r3, r4 );
+ m8 = mul32x32_64(r0, r8) + mul32x32_64(r1, r7 * 2) + mul32x32_64(r2, r6) + mul32x32_64(r3, r5 * 2) + mul32x32_64(r4, r4 );
+ m9 = mul32x32_64(r0, r9) + mul32x32_64(r1, r8 ) + mul32x32_64(r2, r7) + mul32x32_64(r3, r6 ) + mul32x32_64(r4, r5 * 2);
+
+ d6 = r6 * 19;
+ d7 = r7 * 2 * 19;
+ d8 = r8 * 19;
+ d9 = r9 * 2 * 19;
+
+ m0 += (mul32x32_64(d9, r1 ) + mul32x32_64(d8, r2 ) + mul32x32_64(d7, r3 ) + mul32x32_64(d6, r4 * 2) + mul32x32_64(r5, r5 * 2 * 19));
+ m1 += (mul32x32_64(d9, r2 / 2) + mul32x32_64(d8, r3 ) + mul32x32_64(d7, r4 ) + mul32x32_64(d6, r5 * 2));
+ m2 += (mul32x32_64(d9, r3 ) + mul32x32_64(d8, r4 * 2) + mul32x32_64(d7, r5 * 2) + mul32x32_64(d6, r6 ));
+ m3 += (mul32x32_64(d9, r4 ) + mul32x32_64(d8, r5 * 2) + mul32x32_64(d7, r6 ));
+ m4 += (mul32x32_64(d9, r5 * 2) + mul32x32_64(d8, r6 * 2) + mul32x32_64(d7, r7 ));
+ m5 += (mul32x32_64(d9, r6 ) + mul32x32_64(d8, r7 * 2));
+ m6 += (mul32x32_64(d9, r7 * 2) + mul32x32_64(d8, r8 ));
+ m7 += (mul32x32_64(d9, r8 ));
+ m8 += (mul32x32_64(d9, r9 ));
+
+ r0 = (uint32_t)m0 & reduce_mask_26; c = (m0 >> 26);
+ m1 += c; r1 = (uint32_t)m1 & reduce_mask_25; c = (m1 >> 25);
+ m2 += c; r2 = (uint32_t)m2 & reduce_mask_26; c = (m2 >> 26);
+ m3 += c; r3 = (uint32_t)m3 & reduce_mask_25; c = (m3 >> 25);
+ m4 += c; r4 = (uint32_t)m4 & reduce_mask_26; c = (m4 >> 26);
+ m5 += c; r5 = (uint32_t)m5 & reduce_mask_25; c = (m5 >> 25);
+ m6 += c; r6 = (uint32_t)m6 & reduce_mask_26; c = (m6 >> 26);
+ m7 += c; r7 = (uint32_t)m7 & reduce_mask_25; c = (m7 >> 25);
+ m8 += c; r8 = (uint32_t)m8 & reduce_mask_26; c = (m8 >> 26);
+ m9 += c; r9 = (uint32_t)m9 & reduce_mask_25; p = (uint32_t)(m9 >> 25);
+ m0 = r0 + mul32x32_64(p,19); r0 = (uint32_t)m0 & reduce_mask_26; p = (uint32_t)(m0 >> 26);
+ r1 += p;
+ } while (--count);
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+ out[5] = r5;
+ out[6] = r6;
+ out[7] = r7;
+ out[8] = r8;
+ out[9] = r9;
+}
+
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void
+curve25519_expand(bignum25519 out, const unsigned char in[32]) {
+ static const union { uint8_t b[2]; uint16_t s; } endian_check = {{1,0}};
+ uint32_t x0,x1,x2,x3,x4,x5,x6,x7;
+
+ if (endian_check.s == 1) {
+ x0 = *(uint32_t *)(in + 0);
+ x1 = *(uint32_t *)(in + 4);
+ x2 = *(uint32_t *)(in + 8);
+ x3 = *(uint32_t *)(in + 12);
+ x4 = *(uint32_t *)(in + 16);
+ x5 = *(uint32_t *)(in + 20);
+ x6 = *(uint32_t *)(in + 24);
+ x7 = *(uint32_t *)(in + 28);
+ } else {
+ #define F(s) \
+ ((((uint32_t)in[s + 0]) ) | \
+ (((uint32_t)in[s + 1]) << 8) | \
+ (((uint32_t)in[s + 2]) << 16) | \
+ (((uint32_t)in[s + 3]) << 24))
+ x0 = F(0);
+ x1 = F(4);
+ x2 = F(8);
+ x3 = F(12);
+ x4 = F(16);
+ x5 = F(20);
+ x6 = F(24);
+ x7 = F(28);
+ #undef F
+ }
+
+ out[0] = ( x0 ) & reduce_mask_26;
+ out[1] = ((((uint64_t)x1 << 32) | x0) >> 26) & reduce_mask_25;
+ out[2] = ((((uint64_t)x2 << 32) | x1) >> 19) & reduce_mask_26;
+ out[3] = ((((uint64_t)x3 << 32) | x2) >> 13) & reduce_mask_25;
+ out[4] = (( x3) >> 6) & reduce_mask_26;
+ out[5] = ( x4 ) & reduce_mask_25;
+ out[6] = ((((uint64_t)x5 << 32) | x4) >> 25) & reduce_mask_26;
+ out[7] = ((((uint64_t)x6 << 32) | x5) >> 19) & reduce_mask_25;
+ out[8] = ((((uint64_t)x7 << 32) | x6) >> 12) & reduce_mask_26;
+ out[9] = (( x7) >> 6) & reduce_mask_25; /* ignore the top bit */
+}
+
+/* Take a fully reduced polynomial form number and contract it into a little-endian, 32-byte array */
+static void
+curve25519_contract(unsigned char out[32], const bignum25519 in) {
+ bignum25519 f;
+ curve25519_copy(f, in);
+
+ #define carry_pass() \
+ f[1] += f[0] >> 26; f[0] &= reduce_mask_26; \
+ f[2] += f[1] >> 25; f[1] &= reduce_mask_25; \
+ f[3] += f[2] >> 26; f[2] &= reduce_mask_26; \
+ f[4] += f[3] >> 25; f[3] &= reduce_mask_25; \
+ f[5] += f[4] >> 26; f[4] &= reduce_mask_26; \
+ f[6] += f[5] >> 25; f[5] &= reduce_mask_25; \
+ f[7] += f[6] >> 26; f[6] &= reduce_mask_26; \
+ f[8] += f[7] >> 25; f[7] &= reduce_mask_25; \
+ f[9] += f[8] >> 26; f[8] &= reduce_mask_26;
+
+ #define carry_pass_full() \
+ carry_pass() \
+ f[0] += 19 * (f[9] >> 25); f[9] &= reduce_mask_25;
+
+ #define carry_pass_final() \
+ carry_pass() \
+ f[9] &= reduce_mask_25;
+
+ carry_pass_full()
+ carry_pass_full()
+
+ /* now t is between 0 and 2^255-1, properly carried. */
+ /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
+ f[0] += 19;
+ carry_pass_full()
+
+ /* now between 19 and 2^255-1 in both cases, and offset by 19. */
+ f[0] += (1 << 26) - 19;
+ f[1] += (1 << 25) - 1;
+ f[2] += (1 << 26) - 1;
+ f[3] += (1 << 25) - 1;
+ f[4] += (1 << 26) - 1;
+ f[5] += (1 << 25) - 1;
+ f[6] += (1 << 26) - 1;
+ f[7] += (1 << 25) - 1;
+ f[8] += (1 << 26) - 1;
+ f[9] += (1 << 25) - 1;
+
+ /* now between 2^255 and 2^256-20, and offset by 2^255. */
+ carry_pass_final()
+
+ #undef carry_pass
+ #undef carry_full
+ #undef carry_final
+
+ f[1] <<= 2;
+ f[2] <<= 3;
+ f[3] <<= 5;
+ f[4] <<= 6;
+ f[6] <<= 1;
+ f[7] <<= 3;
+ f[8] <<= 4;
+ f[9] <<= 6;
+
+ #define F(i, s) \
+ out[s+0] |= (unsigned char )(f[i] & 0xff); \
+ out[s+1] = (unsigned char )((f[i] >> 8) & 0xff); \
+ out[s+2] = (unsigned char )((f[i] >> 16) & 0xff); \
+ out[s+3] = (unsigned char )((f[i] >> 24) & 0xff);
+
+ out[0] = 0;
+ out[16] = 0;
+ F(0,0);
+ F(1,3);
+ F(2,6);
+ F(3,9);
+ F(4,12);
+ F(5,16);
+ F(6,19);
+ F(7,22);
+ F(8,25);
+ F(9,28);
+ #undef F
+}
+
+/*
+ * Swap the contents of [qx] and [qpx] iff @swap is non-zero
+ */
+DONNA_INLINE static void
+curve25519_swap_conditional(bignum25519 x, bignum25519 qpx, uint32_t iswap) {
+ const uint32_t swap = (uint32_t)(-(int32_t)iswap);
+ uint32_t x0,x1,x2,x3,x4,x5,x6,x7,x8,x9;
+
+ x0 = swap & (x[0] ^ qpx[0]); x[0] ^= x0; qpx[0] ^= x0;
+ x1 = swap & (x[1] ^ qpx[1]); x[1] ^= x1; qpx[1] ^= x1;
+ x2 = swap & (x[2] ^ qpx[2]); x[2] ^= x2; qpx[2] ^= x2;
+ x3 = swap & (x[3] ^ qpx[3]); x[3] ^= x3; qpx[3] ^= x3;
+ x4 = swap & (x[4] ^ qpx[4]); x[4] ^= x4; qpx[4] ^= x4;
+ x5 = swap & (x[5] ^ qpx[5]); x[5] ^= x5; qpx[5] ^= x5;
+ x6 = swap & (x[6] ^ qpx[6]); x[6] ^= x6; qpx[6] ^= x6;
+ x7 = swap & (x[7] ^ qpx[7]); x[7] ^= x7; qpx[7] ^= x7;
+ x8 = swap & (x[8] ^ qpx[8]); x[8] ^= x8; qpx[8] ^= x8;
+ x9 = swap & (x[9] ^ qpx[9]); x[9] ^= x9; qpx[9] ^= x9;
+}
+
diff --git a/src/curve25519-donna-64bit.h b/src/curve25519-donna-64bit.h
new file mode 100644
index 0000000..ec4df52
--- /dev/null
+++ b/src/curve25519-donna-64bit.h
@@ -0,0 +1,345 @@
+typedef uint64_t bignum25519[5];
+
+static const uint64_t reduce_mask_51 = ((uint64_t)1 << 51) - 1;
+static const uint64_t reduce_mask_52 = ((uint64_t)1 << 52) - 1;
+
+/* out = in */
+DONNA_INLINE static void
+curve25519_copy(bignum25519 out, const bignum25519 in) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+ out[4] = in[4];
+}
+
+/* out = a + b */
+DONNA_INLINE static void
+curve25519_add(bignum25519 out, const bignum25519 a, const bignum25519 b) {
+ out[0] = a[0] + b[0];
+ out[1] = a[1] + b[1];
+ out[2] = a[2] + b[2];
+ out[3] = a[3] + b[3];
+ out[4] = a[4] + b[4];
+}
+
+static const uint64_t two54m152 = (((uint64_t)1) << 54) - 152;
+static const uint64_t two54m8 = (((uint64_t)1) << 54) - 8;
+
+/* out = a - b */
+DONNA_INLINE static void
+curve25519_sub(bignum25519 out, const bignum25519 a, const bignum25519 b) {
+ out[0] = a[0] + two54m152 - b[0];
+ out[1] = a[1] + two54m8 - b[1];
+ out[2] = a[2] + two54m8 - b[2];
+ out[3] = a[3] + two54m8 - b[3];
+ out[4] = a[4] + two54m8 - b[4];
+}
+
+
+/* out = (in * scalar) */
+DONNA_INLINE static void
+curve25519_scalar_product(bignum25519 out, const bignum25519 in, const uint64_t scalar) {
+ uint128_t a;
+ uint64_t c;
+
+#if defined(HAVE_NATIVE_UINT128)
+ a = ((uint128_t) in[0]) * scalar; out[0] = (uint64_t)a & reduce_mask_51; c = (uint64_t)(a >> 51);
+ a = ((uint128_t) in[1]) * scalar + c; out[1] = (uint64_t)a & reduce_mask_51; c = (uint64_t)(a >> 51);
+ a = ((uint128_t) in[2]) * scalar + c; out[2] = (uint64_t)a & reduce_mask_51; c = (uint64_t)(a >> 51);
+ a = ((uint128_t) in[3]) * scalar + c; out[3] = (uint64_t)a & reduce_mask_51; c = (uint64_t)(a >> 51);
+ a = ((uint128_t) in[4]) * scalar + c; out[4] = (uint64_t)a & reduce_mask_51; c = (uint64_t)(a >> 51);
+ out[0] += c * 19;
+#else
+ mul64x64_128(a, in[0], scalar) out[0] = lo128(a) & reduce_mask_51; shr128(c, a, 51);
+ mul64x64_128(a, in[1], scalar) add128_64(a, c) out[1] = lo128(a) & reduce_mask_51; shr128(c, a, 51);
+ mul64x64_128(a, in[2], scalar) add128_64(a, c) out[2] = lo128(a) & reduce_mask_51; shr128(c, a, 51);
+ mul64x64_128(a, in[3], scalar) add128_64(a, c) out[3] = lo128(a) & reduce_mask_51; shr128(c, a, 51);
+ mul64x64_128(a, in[4], scalar) add128_64(a, c) out[4] = lo128(a) & reduce_mask_51; shr128(c, a, 51);
+ out[0] += c * 19;
+#endif
+}
+
+/* out = a * b */
+DONNA_INLINE static void
+curve25519_mul(bignum25519 out, const bignum25519 a, const bignum25519 b) {
+#if !defined(HAVE_NATIVE_UINT128)
+ uint128_t mul;
+#endif
+ uint128_t t[5];
+ uint64_t r0,r1,r2,r3,r4,s0,s1,s2,s3,s4,c;
+
+ r0 = b[0];
+ r1 = b[1];
+ r2 = b[2];
+ r3 = b[3];
+ r4 = b[4];
+
+ s0 = a[0];
+ s1 = a[1];
+ s2 = a[2];
+ s3 = a[3];
+ s4 = a[4];
+
+#if defined(HAVE_NATIVE_UINT128)
+ t[0] = ((uint128_t) r0) * s0;
+ t[1] = ((uint128_t) r0) * s1 + ((uint128_t) r1) * s0;
+ t[2] = ((uint128_t) r0) * s2 + ((uint128_t) r2) * s0 + ((uint128_t) r1) * s1;
+ t[3] = ((uint128_t) r0) * s3 + ((uint128_t) r3) * s0 + ((uint128_t) r1) * s2 + ((uint128_t) r2) * s1;
+ t[4] = ((uint128_t) r0) * s4 + ((uint128_t) r4) * s0 + ((uint128_t) r3) * s1 + ((uint128_t) r1) * s3 + ((uint128_t) r2) * s2;
+#else
+ mul64x64_128(t[0], r0, s0)
+ mul64x64_128(t[1], r0, s1) mul64x64_128(mul, r1, s0) add128(t[1], mul)
+ mul64x64_128(t[2], r0, s2) mul64x64_128(mul, r2, s0) add128(t[2], mul) mul64x64_128(mul, r1, s1) add128(t[2], mul)
+ mul64x64_128(t[3], r0, s3) mul64x64_128(mul, r3, s0) add128(t[3], mul) mul64x64_128(mul, r1, s2) add128(t[3], mul) mul64x64_128(mul, r2, s1) add128(t[3], mul)
+ mul64x64_128(t[4], r0, s4) mul64x64_128(mul, r4, s0) add128(t[4], mul) mul64x64_128(mul, r3, s1) add128(t[4], mul) mul64x64_128(mul, r1, s3) add128(t[4], mul) mul64x64_128(mul, r2, s2) add128(t[4], mul)
+#endif
+
+ r1 *= 19;
+ r2 *= 19;
+ r3 *= 19;
+ r4 *= 19;
+
+#if defined(HAVE_NATIVE_UINT128)
+ t[0] += ((uint128_t) r4) * s1 + ((uint128_t) r1) * s4 + ((uint128_t) r2) * s3 + ((uint128_t) r3) * s2;
+ t[1] += ((uint128_t) r4) * s2 + ((uint128_t) r2) * s4 + ((uint128_t) r3) * s3;
+ t[2] += ((uint128_t) r4) * s3 + ((uint128_t) r3) * s4;
+ t[3] += ((uint128_t) r4) * s4;
+#else
+ mul64x64_128(mul, r4, s1) add128(t[0], mul) mul64x64_128(mul, r1, s4) add128(t[0], mul) mul64x64_128(mul, r2, s3) add128(t[0], mul) mul64x64_128(mul, r3, s2) add128(t[0], mul)
+ mul64x64_128(mul, r4, s2) add128(t[1], mul) mul64x64_128(mul, r2, s4) add128(t[1], mul) mul64x64_128(mul, r3, s3) add128(t[1], mul)
+ mul64x64_128(mul, r4, s3) add128(t[2], mul) mul64x64_128(mul, r3, s4) add128(t[2], mul)
+ mul64x64_128(mul, r4, s4) add128(t[3], mul)
+#endif
+
+ r0 = lo128(t[0]) & reduce_mask_51; shr128(c, t[0], 51);
+ add128_64(t[1], c) r1 = lo128(t[1]) & reduce_mask_51; shr128(c, t[1], 51);
+ add128_64(t[2], c) r2 = lo128(t[2]) & reduce_mask_51; shr128(c, t[2], 51);
+ add128_64(t[3], c) r3 = lo128(t[3]) & reduce_mask_51; shr128(c, t[3], 51);
+ add128_64(t[4], c) r4 = lo128(t[4]) & reduce_mask_51; shr128(c, t[4], 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & reduce_mask_51;
+ r1 += c;
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+}
+
+/* out = in^(2 * count) */
+DONNA_INLINE static void
+curve25519_square_times(bignum25519 out, const bignum25519 in, uint64_t count) {
+#if !defined(HAVE_NATIVE_UINT128)
+ uint128_t mul;
+#endif
+ uint128_t t[5];
+ uint64_t r0,r1,r2,r3,r4,c;
+ uint64_t d0,d1,d2,d4,d419;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+
+ do {
+ d0 = r0 * 2;
+ d1 = r1 * 2;
+ d2 = r2 * 2 * 19;
+ d419 = r4 * 19;
+ d4 = d419 * 2;
+
+#if defined(HAVE_NATIVE_UINT128)
+ t[0] = ((uint128_t) r0) * r0 + ((uint128_t) d4) * r1 + (((uint128_t) d2) * (r3 ));
+ t[1] = ((uint128_t) d0) * r1 + ((uint128_t) d4) * r2 + (((uint128_t) r3) * (r3 * 19));
+ t[2] = ((uint128_t) d0) * r2 + ((uint128_t) r1) * r1 + (((uint128_t) d4) * (r3 ));
+ t[3] = ((uint128_t) d0) * r3 + ((uint128_t) d1) * r2 + (((uint128_t) r4) * (d419 ));
+ t[4] = ((uint128_t) d0) * r4 + ((uint128_t) d1) * r3 + (((uint128_t) r2) * (r2 ));
+#else
+ mul64x64_128(t[0], r0, r0) mul64x64_128(mul, d4, r1) add128(t[0], mul) mul64x64_128(mul, d2, r3) add128(t[0], mul)
+ mul64x64_128(t[1], d0, r1) mul64x64_128(mul, d4, r2) add128(t[1], mul) mul64x64_128(mul, r3, r3 * 19) add128(t[1], mul)
+ mul64x64_128(t[2], d0, r2) mul64x64_128(mul, r1, r1) add128(t[2], mul) mul64x64_128(mul, d4, r3) add128(t[2], mul)
+ mul64x64_128(t[3], d0, r3) mul64x64_128(mul, d1, r2) add128(t[3], mul) mul64x64_128(mul, r4, d419) add128(t[3], mul)
+ mul64x64_128(t[4], d0, r4) mul64x64_128(mul, d1, r3) add128(t[4], mul) mul64x64_128(mul, r2, r2) add128(t[4], mul)
+#endif
+
+ r0 = lo128(t[0]) & reduce_mask_51; shr128(c, t[0], 51);
+ add128_64(t[1], c) r1 = lo128(t[1]) & reduce_mask_51; shr128(c, t[1], 51);
+ add128_64(t[2], c) r2 = lo128(t[2]) & reduce_mask_51; shr128(c, t[2], 51);
+ add128_64(t[3], c) r3 = lo128(t[3]) & reduce_mask_51; shr128(c, t[3], 51);
+ add128_64(t[4], c) r4 = lo128(t[4]) & reduce_mask_51; shr128(c, t[4], 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & reduce_mask_51;
+ r1 += c;
+ } while(--count);
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+}
+
+DONNA_INLINE static void
+curve25519_square(bignum25519 out, const bignum25519 in) {
+#if !defined(HAVE_NATIVE_UINT128)
+ uint128_t mul;
+#endif
+ uint128_t t[5];
+ uint64_t r0,r1,r2,r3,r4,c;
+ uint64_t d0,d1,d2,d4,d419;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+
+ d0 = r0 * 2;
+ d1 = r1 * 2;
+ d2 = r2 * 2 * 19;
+ d419 = r4 * 19;
+ d4 = d419 * 2;
+
+#if defined(HAVE_NATIVE_UINT128)
+ t[0] = ((uint128_t) r0) * r0 + ((uint128_t) d4) * r1 + (((uint128_t) d2) * (r3 ));
+ t[1] = ((uint128_t) d0) * r1 + ((uint128_t) d4) * r2 + (((uint128_t) r3) * (r3 * 19));
+ t[2] = ((uint128_t) d0) * r2 + ((uint128_t) r1) * r1 + (((uint128_t) d4) * (r3 ));
+ t[3] = ((uint128_t) d0) * r3 + ((uint128_t) d1) * r2 + (((uint128_t) r4) * (d419 ));
+ t[4] = ((uint128_t) d0) * r4 + ((uint128_t) d1) * r3 + (((uint128_t) r2) * (r2 ));
+#else
+ mul64x64_128(t[0], r0, r0) mul64x64_128(mul, d4, r1) add128(t[0], mul) mul64x64_128(mul, d2, r3) add128(t[0], mul)
+ mul64x64_128(t[1], d0, r1) mul64x64_128(mul, d4, r2) add128(t[1], mul) mul64x64_128(mul, r3, r3 * 19) add128(t[1], mul)
+ mul64x64_128(t[2], d0, r2) mul64x64_128(mul, r1, r1) add128(t[2], mul) mul64x64_128(mul, d4, r3) add128(t[2], mul)
+ mul64x64_128(t[3], d0, r3) mul64x64_128(mul, d1, r2) add128(t[3], mul) mul64x64_128(mul, r4, d419) add128(t[3], mul)
+ mul64x64_128(t[4], d0, r4) mul64x64_128(mul, d1, r3) add128(t[4], mul) mul64x64_128(mul, r2, r2) add128(t[4], mul)
+#endif
+
+ r0 = lo128(t[0]) & reduce_mask_51; shr128(c, t[0], 51);
+ add128_64(t[1], c) r1 = lo128(t[1]) & reduce_mask_51; shr128(c, t[1], 51);
+ add128_64(t[2], c) r2 = lo128(t[2]) & reduce_mask_51; shr128(c, t[2], 51);
+ add128_64(t[3], c) r3 = lo128(t[3]) & reduce_mask_51; shr128(c, t[3], 51);
+ add128_64(t[4], c) r4 = lo128(t[4]) & reduce_mask_51; shr128(c, t[4], 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & reduce_mask_51;
+ r1 += c;
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+}
+
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+DONNA_INLINE static void
+curve25519_expand(bignum25519 out, const unsigned char *in) {
+ static const union { uint8_t b[2]; uint16_t s; } endian_check = {{1,0}};
+ uint64_t x0,x1,x2,x3;
+
+ if (endian_check.s == 1) {
+ x0 = *(uint64_t *)(in + 0);
+ x1 = *(uint64_t *)(in + 8);
+ x2 = *(uint64_t *)(in + 16);
+ x3 = *(uint64_t *)(in + 24);
+ } else {
+ #define F(s) \
+ ((((uint64_t)in[s + 0]) ) | \
+ (((uint64_t)in[s + 1]) << 8) | \
+ (((uint64_t)in[s + 2]) << 16) | \
+ (((uint64_t)in[s + 3]) << 24) | \
+ (((uint64_t)in[s + 4]) << 32) | \
+ (((uint64_t)in[s + 5]) << 40) | \
+ (((uint64_t)in[s + 6]) << 48) | \
+ (((uint64_t)in[s + 7]) << 56))
+
+ x0 = F(0);
+ x1 = F(8);
+ x2 = F(16);
+ x3 = F(24);
+ }
+
+ out[0] = x0 & reduce_mask_51; x0 = (x0 >> 51) | (x1 << 13);
+ out[1] = x0 & reduce_mask_51; x1 = (x1 >> 38) | (x2 << 26);
+ out[2] = x1 & reduce_mask_51; x2 = (x2 >> 25) | (x3 << 39);
+ out[3] = x2 & reduce_mask_51; x3 = (x3 >> 12);
+ out[4] = x3 & reduce_mask_51; /* ignore the top bit */
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array
+ */
+DONNA_INLINE static void
+curve25519_contract(unsigned char *out, const bignum25519 input) {
+ uint64_t t[5];
+ uint64_t f, i;
+
+ t[0] = input[0];
+ t[1] = input[1];
+ t[2] = input[2];
+ t[3] = input[3];
+ t[4] = input[4];
+
+ #define curve25519_contract_carry() \
+ t[1] += t[0] >> 51; t[0] &= reduce_mask_51; \
+ t[2] += t[1] >> 51; t[1] &= reduce_mask_51; \
+ t[3] += t[2] >> 51; t[2] &= reduce_mask_51; \
+ t[4] += t[3] >> 51; t[3] &= reduce_mask_51;
+
+ #define curve25519_contract_carry_full() curve25519_contract_carry() \
+ t[0] += 19 * (t[4] >> 51); t[4] &= reduce_mask_51;
+
+ #define curve25519_contract_carry_final() curve25519_contract_carry() \
+ t[4] &= reduce_mask_51;
+
+ curve25519_contract_carry_full()
+ curve25519_contract_carry_full()
+
+ /* now t is between 0 and 2^255-1, properly carried. */
+ /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
+ t[0] += 19;
+ curve25519_contract_carry_full()
+
+ /* now between 19 and 2^255-1 in both cases, and offset by 19. */
+ t[0] += 0x8000000000000 - 19;
+ t[1] += 0x8000000000000 - 1;
+ t[2] += 0x8000000000000 - 1;
+ t[3] += 0x8000000000000 - 1;
+ t[4] += 0x8000000000000 - 1;
+
+ /* now between 2^255 and 2^256-20, and offset by 2^255. */
+ curve25519_contract_carry_final()
+
+ #define write51full(n,shift) \
+ f = ((t[n] >> shift) | (t[n+1] << (51 - shift))); \
+ for (i = 0; i < 8; i++, f >>= 8) *out++ = (unsigned char)f;
+ #define write51(n) write51full(n,13*n)
+
+ write51(0)
+ write51(1)
+ write51(2)
+ write51(3)
+
+ #undef curve25519_contract_carry
+ #undef curve25519_contract_carry_full
+ #undef curve25519_contract_carry_final
+ #undef write51full
+ #undef write51
+}
+
+/*
+ * Swap the contents of [qx] and [qpx] iff @swap is non-zero
+ */
+DONNA_INLINE static void
+curve25519_swap_conditional(bignum25519 x, bignum25519 qpx, uint64_t iswap) {
+ const uint64_t swap = (uint64_t)(-(int64_t)iswap);
+ uint64_t x0,x1,x2,x3,x4;
+
+ x0 = swap & (x[0] ^ qpx[0]); x[0] ^= x0; qpx[0] ^= x0;
+ x1 = swap & (x[1] ^ qpx[1]); x[1] ^= x1; qpx[1] ^= x1;
+ x2 = swap & (x[2] ^ qpx[2]); x[2] ^= x2; qpx[2] ^= x2;
+ x3 = swap & (x[3] ^ qpx[3]); x[3] ^= x3; qpx[3] ^= x3;
+ x4 = swap & (x[4] ^ qpx[4]); x[4] ^= x4; qpx[4] ^= x4;
+
+}
+
diff --git a/src/curve25519-donna-common.h b/src/curve25519-donna-common.h
new file mode 100644
index 0000000..741fe1b
--- /dev/null
+++ b/src/curve25519-donna-common.h
@@ -0,0 +1,43 @@
+/*
+ * In: b = 2^5 - 2^0
+ * Out: b = 2^250 - 2^0
+ */
+static void
+curve25519_pow_two5mtwo0_two250mtwo0(bignum25519 b) {
+ bignum25519 _ALIGN(16) t0,c;
+
+ /* 2^5 - 2^0 */ /* b */
+ /* 2^10 - 2^5 */ curve25519_square_times(t0, b, 5);
+ /* 2^10 - 2^0 */ curve25519_mul(b, t0, b);
+ /* 2^20 - 2^10 */ curve25519_square_times(t0, b, 10);
+ /* 2^20 - 2^0 */ curve25519_mul(c, t0, b);
+ /* 2^40 - 2^20 */ curve25519_square_times(t0, c, 20);
+ /* 2^40 - 2^0 */ curve25519_mul(t0, t0, c);
+ /* 2^50 - 2^10 */ curve25519_square_times(t0, t0, 10);
+ /* 2^50 - 2^0 */ curve25519_mul(b, t0, b);
+ /* 2^100 - 2^50 */ curve25519_square_times(t0, b, 50);
+ /* 2^100 - 2^0 */ curve25519_mul(c, t0, b);
+ /* 2^200 - 2^100 */ curve25519_square_times(t0, c, 100);
+ /* 2^200 - 2^0 */ curve25519_mul(t0, t0, c);
+ /* 2^250 - 2^50 */ curve25519_square_times(t0, t0, 50);
+ /* 2^250 - 2^0 */ curve25519_mul(b, t0, b);
+}
+
+/*
+ * z^(p - 2) = z(2^255 - 21)
+ */
+static void
+curve25519_recip(bignum25519 out, const bignum25519 z) {
+ bignum25519 _ALIGN(16) a,t0,b;
+
+ /* 2 */ curve25519_square(a, z); /* a = 2 */
+ /* 8 */ curve25519_square_times(t0, a, 2);
+ /* 9 */ curve25519_mul(b, t0, z); /* b = 9 */
+ /* 11 */ curve25519_mul(a, b, a); /* a = 11 */
+ /* 22 */ curve25519_square(t0, a);
+ /* 2^5 - 2^0 = 31 */ curve25519_mul(b, t0, b);
+ /* 2^250 - 2^0 */ curve25519_pow_two5mtwo0_two250mtwo0(b);
+ /* 2^255 - 2^5 */ curve25519_square_times(b, b, 5);
+ /* 2^255 - 21 */ curve25519_mul(out, b, a);
+}
+
diff --git a/src/curve25519-donna-portable-identify.h b/src/curve25519-donna-portable-identify.h
new file mode 100644
index 0000000..26a264c
--- /dev/null
+++ b/src/curve25519-donna-portable-identify.h
@@ -0,0 +1,103 @@
+/* os */
+#if defined(_WIN32) || defined(_WIN64) || defined(__TOS_WIN__) || defined(__WINDOWS__)
+ #define OS_WINDOWS
+#elif defined(sun) || defined(__sun) || defined(__SVR4) || defined(__svr4__)
+ #define OS_SOLARIS
+#else
+ #include <sys/param.h> /* need this to define BSD */
+ #define OS_NIX
+ #if defined(__linux__)
+ #define OS_LINUX
+ #elif defined(BSD)
+ #define OS_BSD
+ #if defined(MACOS_X) || (defined(__APPLE__) & defined(__MACH__))
+ #define OS_OSX
+ #elif defined(macintosh) || defined(Macintosh)
+ #define OS_MAC
+ #elif defined(__OpenBSD__)
+ #define OS_OPENBSD
+ #endif
+ #endif
+#endif
+
+
+/* compiler */
+#if defined(_MSC_VER)
+ #define COMPILER_MSVC
+#endif
+#if defined(__ICC)
+ #define COMPILER_INTEL
+#endif
+#if defined(__GNUC__)
+ #if (__GNUC__ >= 3)
+ #define COMPILER_GCC ((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + (__GNUC_PATCHLEVEL__))
+ #else
+ #define COMPILER_GCC ((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) )
+ #endif
+#endif
+#if defined(__PATHCC__)
+ #define COMPILER_PATHCC
+#endif
+#if defined(__clang__)
+ #define COMPILER_CLANG ((__clang_major__ * 10000) + (__clang_minor__ * 100) + (__clang_patchlevel__))
+#endif
+
+
+
+/* cpu */
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__ ) || defined(_M_X64)
+ #define CPU_X86_64
+#elif defined(__i586__) || defined(__i686__) || (defined(_M_IX86) && (_M_IX86 >= 500))
+ #define CPU_X86 500
+#elif defined(__i486__) || (defined(_M_IX86) && (_M_IX86 >= 400))
+ #define CPU_X86 400
+#elif defined(__i386__) || (defined(_M_IX86) && (_M_IX86 >= 300)) || defined(__X86__) || defined(_X86_) || defined(__I86__)
+ #define CPU_X86 300
+#elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64) || defined(__ia64)
+ #define CPU_IA64
+#endif
+
+#if defined(__sparc__) || defined(__sparc) || defined(__sparcv9)
+ #define CPU_SPARC
+ #if defined(__sparcv9)
+ #define CPU_SPARC64
+ #endif
+#endif
+
+#if defined(powerpc) || defined(__PPC__) || defined(__ppc__) || defined(_ARCH_PPC) || defined(__powerpc__) || defined(__powerpc) || defined(POWERPC) || defined(_M_PPC)
+ #define CPU_PPC
+ #if defined(_ARCH_PWR7)
+ #define CPU_POWER7
+ #elif defined(__64BIT__)
+ #define CPU_PPC64
+ #else
+ #define CPU_PPC32
+ #endif
+#endif
+
+#if defined(__hppa__) || defined(__hppa)
+ #define CPU_HPPA
+#endif
+
+#if defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA)
+ #define CPU_ALPHA
+#endif
+
+/* 64 bit cpu */
+#if defined(CPU_X86_64) || defined(CPU_IA64) || defined(CPU_SPARC64) || defined(__64BIT__) || defined(__LP64__) || defined(_LP64) || (defined(_MIPS_SZLONG) && (_MIPS_SZLONG == 64))
+ #define CPU_64BITS
+#endif
+
+#if defined(COMPILER_MSVC)
+ typedef signed char int8_t;
+ typedef unsigned char uint8_t;
+ typedef signed short int16_t;
+ typedef unsigned short uint16_t;
+ typedef signed int int32_t;
+ typedef unsigned int uint32_t;
+ typedef signed __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+#else
+ #include <stdint.h>
+#endif
+
diff --git a/src/curve25519-donna-portable.h b/src/curve25519-donna-portable.h
new file mode 100644
index 0000000..c0207ab
--- /dev/null
+++ b/src/curve25519-donna-portable.h
@@ -0,0 +1,92 @@
+#include "curve25519-donna-portable-identify.h"
+
+#define mul32x32_64(a,b) (((uint64_t)(a))*(b))
+
+/* platform */
+#if defined(COMPILER_MSVC)
+ #include <intrin.h>
+ #if !defined(_DEBUG)
+ #undef mul32x32_64
+ #define mul32x32_64(a,b) __emulu(a,b)
+ #endif
+ #undef inline
+ #define inline __forceinline
+ #define DONNA_INLINE __forceinline
+ #define DONNA_NOINLINE __declspec(noinline)
+ #define _ALIGN(x) __declspec(align(x))
+ #define ROTL32(a,b) _rotl(a,b)
+ #define ROTR32(a,b) _rotr(a,b)
+#else
+ #include <sys/param.h>
+ #define DONNA_INLINE inline __attribute__((always_inline))
+ #define DONNA_NOINLINE __attribute__((noinline))
+ #define _ALIGN(x) __attribute__((aligned(x)))
+ #define ROTL32(a,b) (((a) << (b)) | ((a) >> (32 - b)))
+ #define ROTR32(a,b) (((a) >> (b)) | ((a) << (32 - b)))
+#endif
+
+/* uint128_t */
+#if defined(CPU_64BITS) && !defined(ED25519_FORCE_32BIT)
+ #if defined(COMPILER_CLANG) && (COMPILER_CLANG >= 30100)
+ #define HAVE_NATIVE_UINT128
+ typedef unsigned __int128 uint128_t;
+ #elif defined(COMPILER_MSVC)
+ #define HAVE_UINT128
+ typedef struct uint128_t {
+ uint64_t lo, hi;
+ } uint128_t;
+ #define mul64x64_128(out,a,b) out.lo = _umul128(a,b,&out.hi);
+ #define shr128_pair(out,hi,lo,shift) out = __shiftright128(lo, hi, shift);
+ #define shl128_pair(out,hi,lo,shift) out = __shiftleft128(lo, hi, shift);
+ #define shr128(out,in,shift) shr128_pair(out, in.hi, in.lo, shift)
+ #define shl128(out,in,shift) shl128_pair(out, in.hi, in.lo, shift)
+ #define add128(a,b) { uint64_t p = a.lo; a.lo += b.lo; a.hi += b.hi + (a.lo < p); }
+ #define add128_64(a,b) { uint64_t p = a.lo; a.lo += b; a.hi += (a.lo < p); }
+ #define lo128(a) (a.lo)
+ #define hi128(a) (a.hi)
+ #elif defined(COMPILER_GCC) && !defined(HAVE_NATIVE_UINT128)
+ #if defined(__SIZEOF_INT128__)
+ #define HAVE_NATIVE_UINT128
+ typedef unsigned __int128 uint128_t;
+ #elif (COMPILER_GCC >= 40400)
+ #define HAVE_NATIVE_UINT128
+ typedef unsigned uint128_t __attribute__((mode(TI)));
+ #elif defined(CPU_X86_64)
+ #define HAVE_UINT128
+ typedef struct uint128_t {
+ uint64_t lo, hi;
+ } uint128_t;
+ #define mul64x64_128(out,a,b) __asm__ ("mulq %3" : "=a" (out.lo), "=d" (out.hi) : "a" (a), "rm" (b));
+ #define shr128_pair(out,hi,lo,shift) __asm__ ("shrdq %2,%1,%0" : "+r" (lo) : "r" (hi), "J" (shift)); out = lo;
+ #define shl128_pair(out,hi,lo,shift) __asm__ ("shldq %2,%1,%0" : "+r" (hi) : "r" (lo), "J" (shift)); out = hi;
+ #define shr128(out,in,shift) shr128_pair(out,in.hi, in.lo, shift)
+ #define shl128(out,in,shift) shl128_pair(out,in.hi, in.lo, shift)
+ #define add128(a,b) __asm__ ("addq %4,%2; adcq %5,%3" : "=r" (a.hi), "=r" (a.lo) : "1" (a.lo), "0" (a.hi), "rm" (b.lo), "rm" (b.hi) : "cc");
+ #define add128_64(a,b) __asm__ ("addq %4,%2; adcq $0,%3" : "=r" (a.hi), "=r" (a.lo) : "1" (a.lo), "0" (a.hi), "rm" (b) : "cc");
+ #define lo128(a) (a.lo)
+ #define hi128(a) (a.hi)
+ #endif
+ #endif
+
+ #if defined(HAVE_NATIVE_UINT128)
+ #define HAVE_UINT128
+ #define mul64x64_128(out,a,b) out = (uint128_t)a * b;
+ #define shr128_pair(out,hi,lo,shift) out = (uint64_t)((((uint128_t)hi << 64) | lo) >> (shift));
+ #define shl128_pair(out,hi,lo,shift) out = (uint64_t)(((((uint128_t)hi << 64) | lo) << (shift)) >> 64);
+ #define shr128(out,in,shift) out = (uint64_t)(in >> (shift));
+ #define shl128(out,in,shift) out = (uint64_t)((in << shift) >> 64);
+ #define add128(a,b) a += b;
+ #define add128_64(a,b) a += (uint64_t)b;
+ #define lo128(a) ((uint64_t)a)
+ #define hi128(a) ((uint64_t)(a >> 64))
+ #endif
+
+ #if !defined(HAVE_UINT128)
+ #error Need a uint128_t implementation!
+ #endif
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+
diff --git a/src/curve25519-donna-scalarmult-base.h b/src/curve25519-donna-scalarmult-base.h
new file mode 100644
index 0000000..061759f
--- /dev/null
+++ b/src/curve25519-donna-scalarmult-base.h
@@ -0,0 +1,66 @@
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * mypublic: the packed little endian x coordinate of the resulting curve point
+ * n: a little endian, 32-byte number
+ * basepoint: a packed little endian point of the curve
+ */
+
+static void
+curve25519_scalarmult_donna(curve25519_key mypublic, const curve25519_key n, const curve25519_key basepoint) {
+ bignum25519 nqpqx = {1}, nqpqz = {0}, nqz = {1}, nqx;
+ bignum25519 q, qx, qpqx, qqx, zzz, zmone;
+ size_t bit, lastbit;
+ int32_t i;
+
+ curve25519_expand(q, basepoint);
+ curve25519_copy(nqx, q);
+
+ /* bit 255 is always 0, and bit 254 is always 1, so skip bit 255 and
+ start pre-swapped on bit 254 */
+ lastbit = 1;
+
+ /* we are doing bits 254..3 in the loop, but are swapping in bits 253..2 */
+ for (i = 253; i >= 2; i--) {
+ curve25519_add(qx, nqx, nqz);
+ curve25519_sub(nqz, nqx, nqz);
+ curve25519_add(qpqx, nqpqx, nqpqz);
+ curve25519_sub(nqpqz, nqpqx, nqpqz);
+ curve25519_mul(nqpqx, qpqx, nqz);
+ curve25519_mul(nqpqz, qx, nqpqz);
+ curve25519_add(qqx, nqpqx, nqpqz);
+ curve25519_sub(nqpqz, nqpqx, nqpqz);
+ curve25519_square(nqpqz, nqpqz);
+ curve25519_square(nqpqx, qqx);
+ curve25519_mul(nqpqz, nqpqz, q);
+ curve25519_square(qx, qx);
+ curve25519_square(nqz, nqz);
+ curve25519_mul(nqx, qx, nqz);
+ curve25519_sub(nqz, qx, nqz);
+ curve25519_scalar_product(zzz, nqz, 121665);
+ curve25519_add(zzz, zzz, qx);
+ curve25519_mul(nqz, nqz, zzz);
+
+ bit = (n[i/8] >> (i & 7)) & 1;
+ curve25519_swap_conditional(nqx, nqpqx, bit ^ lastbit);
+ curve25519_swap_conditional(nqz, nqpqz, bit ^ lastbit);
+ lastbit = bit;
+ }
+
+ /* the final 3 bits are always zero, so we only need to double */
+ for (i = 0; i < 3; i++) {
+ curve25519_add(qx, nqx, nqz);
+ curve25519_sub(nqz, nqx, nqz);
+ curve25519_square(qx, qx);
+ curve25519_square(nqz, nqz);
+ curve25519_mul(nqx, qx, nqz);
+ curve25519_sub(nqz, qx, nqz);
+ curve25519_scalar_product(zzz, nqz, 121665);
+ curve25519_add(zzz, zzz, qx);
+ curve25519_mul(nqz, nqz, zzz);
+ }
+
+ curve25519_recip(zmone, nqz);
+ curve25519_mul(nqz, nqx, zmone);
+ curve25519_contract(mypublic, nqz);
+}
+
diff --git a/src/curve25519-donna.h b/src/curve25519-donna.h
new file mode 100644
index 0000000..e707e22
--- /dev/null
+++ b/src/curve25519-donna.h
@@ -0,0 +1,32 @@
+#include "curve25519.h"
+#include "curve25519-donna-portable.h"
+
+#if defined(CURVE25519_SSE2)
+#else
+ #if defined(HAVE_UINT128) && !defined(CURVE25519_FORCE_32BIT)
+ #define CURVE25519_64BIT
+ #else
+ #define CURVE25519_32BIT
+ #endif
+#endif
+
+#if !defined(CURVE25519_NO_INLINE_ASM)
+#endif
+
+
+#if defined(CURVE25519_SSE2)
+ #include "curve25519-donna-sse2.h"
+#elif defined(CURVE25519_64BIT)
+ #include "curve25519-donna-64bit.h"
+#else
+ #include "curve25519-donna-32bit.h"
+#endif
+
+#include "curve25519-donna-common.h"
+
+#if defined(CURVE25519_SSE2)
+ #include "curve25519-donna-scalarmult-sse2.h"
+#else
+ #include "curve25519-donna-scalarmult-base.h"
+#endif
+
diff --git a/src/curve25519.c b/src/curve25519.c
new file mode 100644
index 0000000..bfd2f58
--- /dev/null
+++ b/src/curve25519.c
@@ -0,0 +1,27 @@
+#include "curve25519-donna.h"
+
+#if !defined(CURVE25519_SUFFIX)
+#define CURVE25519_SUFFIX
+#endif
+
+#define CURVE25519_FN3(fn,suffix) fn##suffix
+#define CURVE25519_FN2(fn,suffix) CURVE25519_FN3(fn,suffix)
+#define CURVE25519_FN(fn) CURVE25519_FN2(fn,CURVE25519_SUFFIX)
+
+void
+CURVE25519_FN(curve25519_donna) (curve25519_key mypublic, const curve25519_key secret, const curve25519_key basepoint) {
+ curve25519_key e;
+ size_t i;
+
+ for (i = 0;i < 32;++i) e[i] = secret[i];
+ e[0] &= 0xf8;
+ e[31] &= 0x7f;
+ e[31] |= 0x40;
+ curve25519_scalarmult_donna(mypublic, e, basepoint);
+}
+
+void
+CURVE25519_FN(curve25519_donna_basepoint) (curve25519_key mypublic, const curve25519_key secret) {
+ static const curve25519_key basepoint = {9};
+ CURVE25519_FN(curve25519_donna)(mypublic, secret, basepoint);
+}
diff --git a/src/curve25519.h b/src/curve25519.h
new file mode 100644
index 0000000..51edd1e
--- /dev/null
+++ b/src/curve25519.h
@@ -0,0 +1,10 @@
+#ifndef CURVE25519_H
+#define CURVE25519_H
+
+typedef unsigned char curve25519_key[32];
+
+void curve25519_donna(curve25519_key mypublic, const curve25519_key secret, const curve25519_key basepoint);
+void curve25519_donna_basepoint(curve25519_key mypublic, const curve25519_key secret);
+
+#endif /* CURVE25519_H */
+
diff --git a/src/inexact.c b/src/inexact.c
new file mode 100644
index 0000000..231eb3f
--- /dev/null
+++ b/src/inexact.c
@@ -0,0 +1,1033 @@
+/* Inexact source code package.
+ *
+ * Written in 2019 by <ben@hackade.org>.
+ *
+ * To the extent possible under law, the author have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "inexact.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h> // for chmod only
+#include <unistd.h>
+#include "argon2.h"
+#include "base64.h"
+#include "chacha20_drng.h"
+#include "curve25519.h"
+#include "norx_inexact.h"
+#include "readpassphrase.h"
+#include "sha3.h"
+
+/*
+ * Generate a random private key and derive the X25519 public key.
+ * Store result in base64 to files.
+ *
+ */
+int generate_keys(const char *seckey_filename, const char *pubkey_filename,
+ int no_password) {
+ unsigned char *privatekey_b64 = NULL;
+ unsigned char *publickey_b64 = NULL;
+ unsigned char *privatekey_b64t = NULL;
+ unsigned char *publickey_b64t = NULL;
+ unsigned char salt[32] = {0};
+ unsigned char privatekey_buffer[64] = {0};
+
+ char password[256] = {0};
+ char *password_out = NULL;
+ char password_verif[256] = {0};
+ char *password_verif_out = NULL;
+ size_t password_len = 0;
+ size_t password_verif_len = 0;
+
+ const uint32_t t_cost = 3;
+ const uint32_t m_cost = (1 << 12);
+ const uint32_t parallelism = 1;
+
+ size_t private_base64_len = 0;
+ size_t private_b64t_len = 0;
+ size_t public_base64_len = 0;
+ size_t public_b64t_len = 0;
+
+ FILE *fs = NULL;
+ FILE *fp = NULL;
+
+ struct chacha20_drng *drng = NULL;
+
+ int exitcode = 1;
+
+ curve25519_key privatekey;
+ curve25519_key publickey;
+
+ /* Secret (or private) key */
+
+ if (no_password) {
+ int ret = drng_chacha20_init(&drng);
+ if (ret) {
+ printf("Chacha20 allocation failed: %d\n", ret);
+ goto exit;
+ }
+ if (drng_chacha20_get(drng, privatekey, sizeof(curve25519_key))) {
+ printf("Getting random numbers failed\n");
+ goto exit;
+ }
+
+ curve25519_donna_basepoint(publickey, privatekey);
+
+ memcpy(privatekey_buffer, privatekey, sizeof(curve25519_key));
+ memcpy(privatekey_buffer + sizeof(curve25519_key), publickey,
+ sizeof(curve25519_key));
+ } else {
+ struct chacha20_drng *drng;
+ int ret = drng_chacha20_init(&drng);
+ if (ret) {
+ printf("Chacha20 allocation failed: %d\n", ret);
+ goto exit;
+ }
+ if (drng_chacha20_get(drng, salt, sizeof(salt))) {
+ printf("Getting random numbers failed\n");
+ goto exit;
+ }
+
+ password_out =
+ readpassphrase("Password : ", password, sizeof(password), 0);
+ if (password_out != password) {
+ printf("password input failed.\n");
+ goto exit;
+ }
+ password_len = strlen(password);
+
+ password_verif_out =
+ readpassphrase("Verifying, please re-enter : ", password_verif,
+ sizeof(password_verif), 0);
+ if (password_verif_out != password_verif) {
+ printf("password input failed.\n");
+ goto exit;
+ }
+ password_verif_len = strlen(password_verif);
+
+ if (password_len != password_verif_len) {
+ printf("Mismatch.\n");
+ goto exit;
+ }
+
+ if (memcmp(password, password_verif, password_len) != 0) {
+ printf("Mismatch.\n");
+ goto exit;
+ }
+
+ int a2res = argon2id_hash_raw(t_cost, m_cost, parallelism, password,
+ password_len, salt, sizeof(salt),
+ privatekey, sizeof(curve25519_key));
+ if (a2res != ARGON2_OK) {
+ printf("argon2 failed.");
+ goto exit;
+ }
+
+ curve25519_donna_basepoint(publickey, privatekey);
+ memcpy(privatekey_buffer, salt, sizeof(salt));
+ memcpy(privatekey_buffer + sizeof(salt), publickey,
+ sizeof(curve25519_key));
+ }
+
+ privatekey_b64 = base64_encode(privatekey_buffer, sizeof(privatekey_buffer),
+ &private_base64_len);
+ if (privatekey_b64 == NULL) {
+ printf("base64 encoding failed.\n");
+ goto exit;
+ }
+
+ privatekey_b64t =
+ b64t_encode(privatekey_b64, private_base64_len, &private_b64t_len);
+ if (privatekey_b64t == NULL) {
+ printf("b64t encoding failed.\n");
+ goto exit;
+ }
+
+ fs = fopen(seckey_filename, "wb");
+ if (fs == NULL) {
+ printf("secret key file opening failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ ssize_t slen = fwrite(privatekey_b64t, 1, private_b64t_len, fs);
+ if (slen != private_b64t_len) {
+ printf("secret key file writing failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+ if (fwrite("\n", 1, 1, fs) != 1) {
+ printf("public key file writing failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ int res = chmod(seckey_filename, S_IRUSR | S_IWUSR);
+ if (res != 0) {
+ printf("secret key file chmod failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ /* Public key */
+
+ publickey_b64 =
+ base64_encode(publickey, sizeof(curve25519_key), &public_base64_len);
+
+ if (publickey_b64 == NULL) {
+ printf("base64 encoding failed.\n");
+ goto exit;
+ }
+
+ publickey_b64t =
+ b64t_encode(publickey_b64, public_base64_len, &public_b64t_len);
+
+ if (publickey_b64t == NULL) {
+ printf("b64t encoding failed.\n");
+ goto exit;
+ }
+
+ fp = fopen(pubkey_filename, "wb");
+ if (fp == NULL) {
+ printf("public key file opening failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ ssize_t plen = fwrite(publickey_b64t, 1, public_b64t_len, fp);
+ if (plen != public_b64t_len) {
+ printf("public key file writing failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+ if (fwrite("\n", 1, 1, fp) != 1) {
+ printf("public key file writing failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ exitcode = 0;
+
+exit:
+ drng_chacha20_destroy(drng);
+
+ memset(privatekey_b64, 0, private_base64_len);
+ memset(privatekey_b64t, 0, private_b64t_len);
+ memset(privatekey, 0, sizeof(curve25519_key));
+ memset(publickey_b64, 0, public_base64_len);
+ memset(publickey_b64t, 0, public_b64t_len);
+ memset(publickey, 0, sizeof(curve25519_key));
+ memset(salt, 0, sizeof(salt));
+ memset(privatekey_buffer, 0, sizeof(privatekey_buffer));
+ memset(password, 0, sizeof(password));
+ memset(password_verif, 0, sizeof(password_verif));
+
+ free(privatekey_b64);
+ free(publickey_b64);
+ free(privatekey_b64t);
+ free(publickey_b64t);
+
+ if (fs != NULL) {
+ fclose(fs);
+ }
+ if (fp != NULL) {
+ fclose(fp);
+ }
+
+ return exitcode;
+}
+
+/*
+ * Return key file content in key variable.
+ *
+ */
+int get_seckey(const char *keyfile, unsigned char *skey, unsigned char *pkey) {
+ unsigned char *base64_decoded = NULL;
+ unsigned char *b64t_decoded = NULL;
+
+ size_t base64_decoded_len = 0;
+ size_t b64t_decoded_len = 0;
+ size_t password_len = 0;
+
+ FILE *fs = NULL;
+
+ unsigned char salt[32] = {0};
+ char password[256] = {0};
+ char *password_out = NULL;
+ curve25519_key pubkey;
+ curve25519_key pubkey_from_secret;
+ curve25519_key seckey;
+
+ const uint32_t t_cost = 3;
+ const uint32_t m_cost = (1 << 12);
+ const uint32_t parallelism = 1;
+
+ int exitcode = 1;
+
+ fs = fopen(keyfile, "rb");
+ if (fs == NULL) {
+ printf("key file opening failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ int rsb = fseek(fs, 0L, SEEK_END);
+ if (rsb != 0) {
+ printf("seek to end file '%s' failed: %s.\n", keyfile, strerror(errno));
+ goto exit;
+ }
+
+ size_t sz = ftell(fs);
+ if (sz == -1) {
+ printf("tell file '%s' failed: %s.\n", keyfile, strerror(errno));
+ goto exit;
+ }
+
+ int rse = fseek(fs, 0L, SEEK_SET);
+ if (rse != 0) {
+ printf("seek file to begin'%s' failed: %s.\n", keyfile,
+ strerror(errno));
+ goto exit;
+ }
+
+ /* max_size = base64(sizeof(curve25519_key)) = 64 * 4 / 3 + 1 -> 86 */
+ unsigned char file_data[87] = {0};
+
+ size_t readed = fread(&file_data, 1, sz, fs);
+ if (readed != sz) {
+ printf("read file '%s' failed: %s.\n", keyfile, strerror(errno));
+ goto exit;
+ }
+
+ b64t_decoded = b64t_decode(file_data, readed, &b64t_decoded_len);
+ if (b64t_decoded == NULL) {
+ printf("b64t decoding failed.\n");
+ goto exit;
+ }
+
+ base64_decoded =
+ base64_decode(b64t_decoded, b64t_decoded_len, &base64_decoded_len);
+ if (base64_decoded == NULL) {
+ printf("base64 decoding failed.\n");
+ goto exit;
+ }
+
+ if (base64_decoded_len != 64) {
+ printf("decoded size mismatch.\n");
+ goto exit;
+ }
+
+ memcpy(seckey, base64_decoded, sizeof(curve25519_key));
+ memcpy(pubkey, base64_decoded + sizeof(curve25519_key),
+ sizeof(curve25519_key));
+ curve25519_donna_basepoint(pubkey_from_secret, seckey);
+
+ int password_protected_flag =
+ (memcmp(pubkey, pubkey_from_secret, sizeof(curve25519_key)) != 0);
+ if (password_protected_flag) {
+ memcpy(salt, base64_decoded, sizeof(salt));
+ password_out =
+ readpassphrase("Password : ", password, sizeof(password), 0);
+ if (password_out != password) {
+ printf("password input failed.\n");
+ goto exit;
+ }
+ password_len = strlen(password);
+
+ int a2res = argon2id_hash_raw(t_cost, m_cost, parallelism, password,
+ password_len, salt, sizeof(salt), seckey,
+ sizeof(curve25519_key));
+ if (a2res != ARGON2_OK) {
+ printf("argon2 failed.");
+ goto exit;
+ }
+ curve25519_donna_basepoint(pubkey_from_secret, seckey);
+ if (memcmp(pubkey_from_secret, pubkey, sizeof(curve25519_key)) != 0) {
+ printf("Bad password\n");
+ goto exit;
+ }
+ }
+
+ memcpy(skey, seckey, sizeof(curve25519_key));
+ if (pkey != NULL) {
+ memcpy(pkey, pubkey_from_secret, sizeof(curve25519_key));
+ }
+ exitcode = 0;
+
+exit:
+ memset(file_data, 0, sizeof(file_data));
+ memset(base64_decoded, 0, base64_decoded_len);
+ memset(b64t_decoded, 0, b64t_decoded_len);
+ memset(salt, 0, sizeof(salt));
+ memset(password, 0, sizeof(password));
+ memset(seckey, 0, sizeof(curve25519_key));
+ memset(pubkey, 0, sizeof(curve25519_key));
+ memset(pubkey_from_secret, 0, sizeof(curve25519_key));
+
+ if (fs != NULL) {
+ fclose(fs);
+ }
+
+ free(base64_decoded);
+ free(b64t_decoded);
+
+ return exitcode;
+}
+
+/*
+ * Return key file content in key variable.
+ *
+ */
+int get_pubkey(const char *keyfile, unsigned char *pkey) {
+ unsigned char *base64_decoded = NULL;
+ unsigned char *b64t_decoded = NULL;
+
+ size_t b64t_decoded_len = 0;
+ size_t base64_decoded_len = 0;
+
+ FILE *fs = NULL;
+
+ curve25519_key pubkey;
+
+ int exitcode = 1;
+
+ fs = fopen(keyfile, "rb");
+ if (fs == NULL) {
+ printf("key file opening failed: %s.\n", strerror(errno));
+ goto exit;
+ }
+
+ int rsb = fseek(fs, 0L, SEEK_END);
+ if (rsb != 0) {
+ printf("seek to end file '%s' failed: %s.\n", keyfile, strerror(errno));
+ goto exit;
+ }
+
+ size_t sz = ftell(fs);
+ if (sz == -1) {
+ printf("tell file '%s' failed: %s.\n", keyfile, strerror(errno));
+ goto exit;
+ }
+
+ int rse = fseek(fs, 0L, SEEK_SET);
+ if (rse != 0) {
+ printf("seek file to begin'%s' failed: %s.\n", keyfile,
+ strerror(errno));
+ goto exit;
+ }
+
+ /* max_size = base64(sizeof(curve25519_key)) = 32 * 4 / 3 + 1 -> 44 */
+ unsigned char file_data[44] = {0};
+ size_t readed = fread(&file_data, 1, sz, fs);
+ if (readed != sz) {
+ printf("read file '%s' failed: %s.\n", keyfile, strerror(errno));
+ goto exit;
+ }
+
+ b64t_decoded = b64t_decode(file_data, readed, &b64t_decoded_len);
+ if (b64t_decoded == NULL) {
+ printf("b64t decoding failed.\n");
+ goto exit;
+ }
+
+ base64_decoded =
+ base64_decode(b64t_decoded, b64t_decoded_len, &base64_decoded_len);
+ if (base64_decoded == NULL) {
+ printf("base64 decoding failed.\n");
+ goto exit;
+ }
+
+ if (base64_decoded_len != 32) {
+ printf("decoded size mismatch.\n");
+ goto exit;
+ }
+
+ memcpy(pkey, base64_decoded, 32);
+
+ exitcode = 0;
+
+exit:
+ memset(file_data, 0, sizeof(file_data));
+ memset(b64t_decoded, 0, b64t_decoded_len);
+ memset(base64_decoded, 0, base64_decoded_len);
+ memset(pubkey, 0, sizeof(curve25519_key));
+
+ if (fs != NULL) {
+ fclose(fs);
+ }
+
+ free(b64t_decoded);
+ free(base64_decoded);
+
+ return exitcode;
+}
+
+/*
+ * Encrypt data, return allocated message in base64.
+ *
+ */
+unsigned char *encrypt_data(const unsigned char *seckey,
+ const unsigned char *pubkey,
+ const unsigned char *salt,
+ const unsigned char *data,
+ size_t data_len, // in bytes
+ size_t rand_len, // in bytes
+ size_t tag1_len, // in bytes
+ int base64_transformation, // 0 or 1
+ size_t *out_encrypted_len) {
+ unsigned char *rand = NULL;
+ unsigned char *encrypted1 = NULL;
+ unsigned char *part1 = NULL;
+ unsigned char *encrypted = NULL;
+ unsigned char *encrypted_b64 = NULL;
+ unsigned char *encrypted_b64t = NULL;
+ unsigned char *out = NULL;
+
+ size_t encrypted1_len = 0;
+ size_t norx_encrypted1_len = 0;
+ size_t tag1_len_bits = tag1_len * 8;
+ size_t part1_len = 0;
+ size_t part0_len = 0;
+ size_t norx_params_encrypted_len = 0;
+ size_t encrypted_b64_len = 0;
+ size_t encrypted_b64t_len = 0;
+ size_t encrypted_len = 0;
+
+ const size_t shared_secret_len = 32;
+ unsigned char shared_secret[32] = {0};
+
+ const size_t nonce1_len = 32;
+ const size_t tag0_len = 4;
+ const size_t tag0_len_bits = tag0_len * 8;
+ const size_t params_len = 5;
+ const size_t params_encrypted_len = 9; // params_len + tag0_len
+ const size_t nonce0_len = 32;
+ const size_t header0_len = 4;
+ const size_t encrypted_len_expected = data_len + tag1_len;
+
+ const uint8_t *key1 = 0;
+ const uint8_t *nonce1 = 0;
+ unsigned char params[5] = {0};
+ unsigned char params_encrypted[9] = {0};
+ const uint8_t *key0 = 0;
+ const uint8_t *nonce0 = 0;
+ unsigned char header0[4] = {0};
+
+ struct chacha20_drng *drng = NULL;
+
+ *out_encrypted_len = 0;
+
+ // generate shared secret from DH X25519
+ curve25519_donna(shared_secret, seckey, pubkey);
+
+ // generate part 1 random data
+ rand = malloc(rand_len);
+ if (rand == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+
+ int ret = drng_chacha20_init(&drng);
+ if (ret) {
+ printf("Chacha20 allocation failed: %d\n", ret);
+ goto exit;
+ }
+ if (drng_chacha20_get(drng, rand, rand_len)) {
+ printf("Getting random numbers failed\n");
+ goto exit;
+ }
+
+ // generate part 0 message
+ part0_len = params_len + tag0_len;
+
+ params[0] = (rand_len >> 24) & 0xFF;
+ params[1] = (rand_len >> 16) & 0xFF;
+ params[2] = (rand_len >> 8) & 0xFF;
+ params[3] = rand_len & 0xFF;
+
+ params[4] = tag1_len;
+
+ // calculate part length
+ encrypted1_len = tag1_len + data_len;
+ part1_len = encrypted1_len + rand_len;
+ encrypted_len = part0_len + part1_len;
+
+ // generate part 0 header
+ header0[0] = (encrypted_len >> 24) & 0xFF;
+ header0[1] = (encrypted_len >> 16) & 0xFF;
+ header0[2] = (encrypted_len >> 8) & 0xFF;
+ header0[3] = encrypted_len & 0xFF;
+
+ // generate nonce 1
+ sha3_context nc1;
+ sha3_Init256(&nc1);
+ sha3_Update(&nc1, params, params_len);
+ sha3_Update(&nc1, rand, rand_len);
+ nonce1 = sha3_Finalize(&nc1);
+
+ // generate key for part1
+ sha3_context kc1;
+ sha3_Init256(&kc1);
+ sha3_Update(&kc1, nonce1, nonce1_len);
+ sha3_Update(&kc1, shared_secret, shared_secret_len);
+ key1 = sha3_Finalize(&kc1);
+
+ // encrypt message data
+ encrypted1 = malloc(encrypted1_len);
+ if (encrypted1 == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+
+ norx_aead_encrypt(encrypted1, &norx_encrypted1_len, params, params_len,
+ data, data_len, NULL, 0, nonce1, key1, tag1_len_bits);
+ if (encrypted1_len != norx_encrypted1_len ||
+ norx_encrypted1_len != encrypted_len_expected) {
+ printf("Norx encryption failed.\n");
+ goto exit;
+ }
+
+ // generate full part 1 buffer
+ part1 = malloc(part1_len);
+ if (part1 == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+ memcpy(part1, rand, rand_len);
+ memcpy((part1 + rand_len), encrypted1, encrypted1_len);
+
+ // generate part 0 nonce: sha3-256(rand+encrypted1)
+ sha3_context nc0;
+ sha3_Init256(&nc0);
+ sha3_Update(&nc0, part1, part1_len);
+ nonce0 = sha3_Finalize(&nc0);
+
+ // generate key for part 0
+ sha3_context kc0;
+ sha3_Init256(&kc0);
+ sha3_Update(&kc0, nonce0, nonce0_len);
+ sha3_Update(&kc0, shared_secret, shared_secret_len);
+ key0 = sha3_Finalize(&kc0);
+
+ // encrypt params (part 0 message)
+ norx_aead_encrypt(params_encrypted, &norx_params_encrypted_len, header0,
+ header0_len, params, params_len, NULL, 0, nonce0, key0,
+ tag0_len_bits);
+ if (params_encrypted_len != norx_params_encrypted_len) {
+ printf("Norx encryption failed.\n");
+ goto exit;
+ }
+
+ // symmetric case
+ if (salt) {
+ encrypted_len = encrypted_len + 64;
+ }
+
+ // generate full message buffer
+ encrypted = malloc(encrypted_len);
+ if (encrypted == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+
+ if (salt) {
+ memcpy(encrypted, salt, 32);
+ memcpy(32 + encrypted, pubkey, 32);
+ memcpy(64 + encrypted, params_encrypted, params_encrypted_len);
+ memcpy(64 + encrypted + params_encrypted_len, part1, part1_len);
+ } else {
+ memcpy(encrypted, params_encrypted, params_encrypted_len);
+ memcpy(encrypted + params_encrypted_len, part1, part1_len);
+ }
+
+ // encode full message in base64
+ encrypted_b64 = base64_encode(encrypted, encrypted_len, &encrypted_b64_len);
+ if (encrypted_b64 == NULL || encrypted_b64_len == 0 ||
+ encrypted_b64_len < encrypted_len) {
+ printf("Base64 encoding failed.\n");
+ goto exit;
+ }
+
+ // transform base64 encoding
+ if (base64_transformation) {
+ encrypted_b64t =
+ b64t_encode(encrypted_b64, encrypted_b64_len, &encrypted_b64t_len);
+ if (encrypted_b64t == NULL || encrypted_b64t_len == 0) {
+ memset(encrypted_b64, 0, encrypted_b64_len);
+ free(encrypted_b64);
+ goto exit;
+ }
+ out = encrypted_b64t;
+ *out_encrypted_len = encrypted_b64t_len;
+ memset(encrypted_b64, 0, encrypted_b64_len);
+ free(encrypted_b64);
+ } else {
+ out = encrypted_b64;
+ *out_encrypted_len = encrypted_b64_len;
+ }
+
+exit:
+ drng_chacha20_destroy(drng);
+
+ memset(rand, 0, rand_len);
+ memset(encrypted1, 0, encrypted1_len);
+ memset(part1, 0, part1_len);
+ memset(encrypted, 0, encrypted_len);
+ memset(params, 0, params_len);
+ memset(params_encrypted, 0, params_encrypted_len);
+ memset(shared_secret, 0, shared_secret_len);
+ memset(header0, 0, header0_len);
+
+ memset(&kc0, 0, sizeof(sha3_context));
+ memset(&kc1, 0, sizeof(sha3_context));
+ memset(&nc0, 0, sizeof(sha3_context));
+ memset(&nc1, 0, sizeof(sha3_context));
+
+ free(rand);
+ free(encrypted1);
+ free(part1);
+ free(encrypted);
+
+ return out;
+}
+
+int get_symmetrickeys(unsigned char *salt_out, unsigned char *seckey_out,
+ unsigned char *pubkey_out) {
+ struct chacha20_drng *drng;
+
+ const uint32_t t_cost = 3;
+ const uint32_t m_cost = (1 << 12);
+ const uint32_t parallelism = 1;
+
+ char password[256] = {0};
+ char *password_out = NULL;
+ char password_verif[256] = {0};
+ char *password_verif_out = NULL;
+ size_t password_len = 0;
+ size_t password_verif_len = 0;
+
+ int exitcode = 1;
+
+ int ret = drng_chacha20_init(&drng);
+ if (ret) {
+ printf("Chacha20 allocation failed: %d\n", ret);
+ goto exit;
+ }
+ if (drng_chacha20_get(drng, salt_out, 32)) {
+ printf("Getting random numbers failed\n");
+ goto exit;
+ }
+
+ password_out = readpassphrase("Password : ", password, sizeof(password), 0);
+ if (password_out != password) {
+ printf("password input failed.\n");
+ goto exit;
+ }
+ password_len = strlen(password);
+
+ password_verif_out =
+ readpassphrase("Verifying, please re-enter : ", password_verif,
+ sizeof(password_verif), 0);
+ if (password_verif_out != password_verif) {
+ printf("password input failed.\n");
+ goto exit;
+ }
+ password_verif_len = strlen(password_verif);
+
+ if (password_len != password_verif_len) {
+ printf("Mismatch.\n");
+ goto exit;
+ }
+
+ if (memcmp(password, password_verif, password_len) != 0) {
+ printf("Mismatch.\n");
+ goto exit;
+ }
+
+ int a2res =
+ argon2id_hash_raw(t_cost, m_cost, parallelism, password, password_len,
+ salt_out, 32, seckey_out, sizeof(curve25519_key));
+ if (a2res != ARGON2_OK) {
+ printf("argon2 failed.");
+ goto exit;
+ }
+
+ curve25519_donna_basepoint(pubkey_out, seckey_out);
+
+ exitcode = 0;
+
+exit:
+ drng_chacha20_destroy(drng);
+ memset(password, 0, sizeof(password));
+ memset(password_verif, 0, sizeof(password_verif));
+
+ return exitcode;
+}
+
+int check_get_symmetrickeys(const unsigned char *data, const size_t data_len,
+ unsigned char *seckey_out,
+ unsigned char *pubkey_out) {
+ const uint32_t t_cost = 3;
+ const uint32_t m_cost = (1 << 12);
+ const uint32_t parallelism = 1;
+
+ char password[256] = {0};
+ char *password_out = NULL;
+ size_t password_len = 0;
+
+ unsigned char *data_b64t_decoded = NULL;
+ unsigned char *encrypted = NULL;
+ size_t data_b64t_decoded_len = 0;
+ size_t encrypted_len = 0;
+
+ unsigned char salt[32] = {0};
+
+ curve25519_key pubkey_from_secret;
+ int exitcode = 1;
+
+ data_b64t_decoded = b64t_decode(data, data_len, &data_b64t_decoded_len);
+
+ encrypted =
+ base64_decode(data_b64t_decoded, data_b64t_decoded_len, &encrypted_len);
+ if (encrypted == NULL) {
+ printf("base64 decoding failed.\n");
+ goto exit;
+ }
+
+ memcpy(salt, encrypted, 32);
+ memcpy(pubkey_from_secret, encrypted + 32, 32);
+
+ password_out = readpassphrase("Password : ", password, sizeof(password), 0);
+ if (password_out != password) {
+ printf("password input failed.\n");
+ goto exit;
+ }
+ password_len = strlen(password);
+
+ int a2res =
+ argon2id_hash_raw(t_cost, m_cost, parallelism, password, password_len,
+ salt, 32, seckey_out, sizeof(curve25519_key));
+ if (a2res != ARGON2_OK) {
+ printf("argon2 failed.");
+ goto exit;
+ }
+
+ curve25519_donna_basepoint(pubkey_out, seckey_out);
+ if (memcmp(pubkey_out, pubkey_from_secret, sizeof(curve25519_key)) != 0) {
+ printf("Wrong password\n");
+ memset(seckey_out, 0, sizeof(curve25519_key));
+ memset(salt, 0, 32);
+ memset(pubkey_out, 0, sizeof(curve25519_key));
+ goto exit;
+ }
+
+ exitcode = 0;
+
+exit:
+ memset(password, 0, sizeof(password));
+ memset(encrypted, 0, encrypted_len);
+ memset(data_b64t_decoded, 0, data_b64t_decoded_len);
+
+ free(encrypted);
+ free(data_b64t_decoded);
+
+ return exitcode;
+}
+
+/*
+ * Decrypt data, return allocated clear text message.
+ *
+ */
+unsigned char *decrypt_data(const unsigned char *seckey,
+ const unsigned char *pubkey,
+ const unsigned char *data, size_t data_len,
+ int symmetric_flag, size_t *data_len_out) {
+ unsigned char *data_b64t_decoded = NULL;
+ unsigned char *encrypted = NULL;
+ unsigned char *part1 = NULL;
+ unsigned char *rand = NULL;
+ unsigned char *message = NULL;
+ unsigned char *encrypted1 = NULL;
+
+ size_t shared_secret_len = 32;
+ unsigned char shared_secret[32] = {0};
+
+ size_t encrypted_len = 0;
+ size_t data_b64t_decoded_len = 0;
+ size_t part1_len = 0;
+ size_t rand_len = 0;
+ size_t tag1_len = 0;
+ size_t tag1_len_bits = 0;
+ size_t encrypted1_len = 0;
+ size_t message_len = 0;
+ size_t norx_message_len = 0;
+
+ const size_t tag1_len_min = 4;
+ const size_t rand_len_min = 4;
+ const size_t nonce1_len = 32;
+ const size_t tag0_len = 4;
+ const size_t tag0_len_bits = tag0_len * 8;
+ const size_t part0_len = 9; // params_len + tag0_len;
+ const size_t nonce0_len = 32;
+
+ const uint8_t *nonce0 = 0;
+ const size_t header0_len = 4;
+ unsigned char header0[4] = {0};
+ const uint8_t *key0 = 0;
+ unsigned char encrypted0[9] = {0};
+ const size_t params_len = 5;
+ unsigned char params[5];
+ size_t norx_params_len = 0;
+ const uint8_t *nonce1 = 0;
+ const uint8_t *key1 = 0;
+
+ // shared secret DH X25519
+ curve25519_donna(shared_secret, seckey, pubkey);
+
+ // decode base64
+ data_b64t_decoded = b64t_decode(data, data_len, &data_b64t_decoded_len);
+
+ // decode base64 transformation
+ encrypted =
+ base64_decode(data_b64t_decoded, data_b64t_decoded_len, &encrypted_len);
+ if (encrypted == NULL) {
+ printf("base64 decoding failed.\n");
+ goto exit;
+ }
+ if (encrypted_len < part0_len + tag1_len_min + rand_len_min) {
+ printf("Size mismatch.\n");
+ goto exit;
+ }
+
+ if (symmetric_flag) {
+ encrypted = encrypted + 64;
+ encrypted_len = encrypted_len - 64;
+ }
+
+ // part1 message allocation and copy
+ part1_len = encrypted_len - part0_len;
+ part1 = malloc(part1_len);
+ if (part1 == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+ memcpy(part1, encrypted + part0_len, part1_len);
+
+ // generate part 0 header
+ header0[0] = (encrypted_len >> 24) & 0xFF;
+ header0[1] = (encrypted_len >> 16) & 0xFF;
+ header0[2] = (encrypted_len >> 8) & 0xFF;
+ header0[3] = encrypted_len & 0xFF;
+
+ // nonce 0 generation: sha3-256(part1)
+ sha3_context nc0;
+ sha3_Init256(&nc0);
+ sha3_Update(&nc0, part1, part1_len);
+ nonce0 = sha3_Finalize(&nc0);
+
+ // generate key 0
+ sha3_context kc0;
+ sha3_Init256(&kc0);
+ sha3_Update(&kc0, nonce0, nonce0_len);
+ sha3_Update(&kc0, shared_secret, shared_secret_len);
+ key0 = sha3_Finalize(&kc0);
+
+ // decrypt part 0 part (params)
+ memcpy(encrypted0, encrypted, part0_len);
+
+ int n0 = norx_aead_decrypt(params, &norx_params_len, header0, header0_len,
+ encrypted0, part0_len, NULL, 0, nonce0, key0,
+ tag0_len_bits);
+
+ if (n0 != 0) {
+ printf("Norx0 decryption failed.\n");
+ goto exit;
+ }
+
+ // get part 1 encryption params
+ rand_len =
+ (params[0] << 24) + (params[1] << 16) + (params[2] << 8) + params[3];
+ tag1_len = params[4];
+ tag1_len_bits = tag1_len * 8;
+
+ if (tag1_len != 4 && tag1_len != 8 && tag1_len != 16 && tag1_len != 24 &&
+ tag1_len != 32) {
+ printf("bad auth tag len.\n");
+ goto exit;
+ }
+ if (rand_len > (encrypted_len - part0_len - tag1_len) ||
+ rand_len < rand_len_min) {
+ printf("size mismatch.\n");
+ goto exit;
+ }
+
+ // allocate and copy rand 1
+ rand = malloc(rand_len);
+ if (rand == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+ memcpy(rand, encrypted + part0_len, rand_len);
+
+ // generate nonce1: sha3-256(rand)
+ sha3_context nc1;
+ sha3_Init256(&nc1);
+ sha3_Update(&nc1, params, params_len);
+ sha3_Update(&nc1, rand, rand_len);
+ nonce1 = sha3_Finalize(&nc1);
+
+ // generate key for part1
+ sha3_context kc1;
+ sha3_Init256(&kc1);
+ sha3_Update(&kc1, nonce1, nonce1_len);
+ sha3_Update(&kc1, shared_secret, shared_secret_len);
+ key1 = sha3_Finalize(&kc1);
+
+ // get encrypted message buffer
+ encrypted1 = encrypted + part0_len + rand_len;
+ encrypted1_len = encrypted_len - part0_len - rand_len;
+
+ message_len = encrypted1_len - tag1_len;
+ message = malloc(message_len);
+ if (message == NULL) {
+ printf("malloc failed.\n");
+ goto exit;
+ }
+
+ int n1 = norx_aead_decrypt(message, &norx_message_len, params, params_len,
+ encrypted1, encrypted1_len, NULL, 0, nonce1,
+ key1, tag1_len_bits);
+ if (n1 != 0 || norx_message_len != message_len) {
+ printf("Norx1 decryption failed.\n");
+ goto exit;
+ }
+
+ *data_len_out = message_len;
+
+exit:
+ memset(data_b64t_decoded, 0, data_b64t_decoded_len);
+ memset(encrypted, 0, encrypted_len);
+ memset(part1, 0, part1_len);
+ memset(rand, 0, rand_len);
+ memset(shared_secret, 0, shared_secret_len);
+ memset(encrypted0, 0, part0_len);
+ memset(params, 0, params_len);
+ memset(header0, 0, header0_len);
+
+ memset(&kc0, 0, sizeof(sha3_context));
+ memset(&kc1, 0, sizeof(sha3_context));
+ memset(&nc0, 0, sizeof(sha3_context));
+ memset(&nc1, 0, sizeof(sha3_context));
+
+ free(data_b64t_decoded);
+ if (symmetric_flag)
+ free(encrypted - 64);
+ else
+ free(encrypted);
+ free(part1);
+ free(rand);
+
+ return message;
+ return NULL;
+}
+
diff --git a/src/inexact.h b/src/inexact.h
new file mode 100644
index 0000000..f579704
--- /dev/null
+++ b/src/inexact.h
@@ -0,0 +1,40 @@
+/* Inexact source code package.
+ *
+ * Written in 2019 by <ben@hackade.org>.
+ *
+ * To the extent possible under law, the author have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef INEXACT_H
+#define INEXACT_H
+
+#include <stddef.h>
+
+int generate_keys(const char *seckey_filename, const char *pubkey_filename,
+ int no_password);
+int get_seckey(const char *keyfile, unsigned char *skey, unsigned char *pkey);
+int get_pubkey(const char *keyfile, unsigned char *pkey);
+int get_symmetrickeys(unsigned char *salt_out, unsigned char *seckey_out,
+ unsigned char *pubkey_out);
+int check_get_symmetrickeys(const unsigned char *data, const size_t data_len,
+ unsigned char *seckey_out,
+ unsigned char *pubkey_out);
+unsigned char *encrypt_data(const unsigned char *seckey,
+ const unsigned char *pubkey,
+ const unsigned char *salt,
+ const unsigned char *data, size_t data_len,
+ size_t nonce1_len, size_t tag1_len,
+ int base64_transformation, size_t *encrypted_len);
+
+unsigned char *decrypt_data(const unsigned char *seckey,
+ const unsigned char *pubkey,
+ const unsigned char *data, size_t data_len,
+ int symmetric_flag, size_t *data_len_out);
+
+#endif /* INEXACT_H */
+
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..46a5277
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,555 @@
+/* Inexact source code package.
+ *
+ * Written in 2019 by <ben@hackade.org>.
+ *
+ * To the extent possible under law, the author have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "argtable3.h"
+#include "inexact.h"
+#include "tests.h"
+
+/* global arg_xxx structs */
+struct arg_lit *help, *version, *gen, *dencrypt, *ddecrypt, *test, *base64, *weak,
+ *nopassword, *symmetric;
+struct arg_file *seckey, *pubkey, *infile, *outfile;
+struct arg_end *end;
+struct arg_int *taglen, *noncelen, *cipherlen;
+
+int main(int argc, char *argv[]) {
+ /* the global arg_xxx structs are initialised within the argtable */
+ void *argtable[] = {
+ help = arg_litn("h", "help", 0, 1, "display this help and exit"),
+ version = arg_litn("v", "version", 0, 1, "display version info and exit"),
+ gen = arg_litn("g", "genkeys", 0, 1, "generate keys"),
+ dencrypt = arg_litn("e", "encrypt", 0, 1, "encrypt data"),
+ ddecrypt = arg_litn("d", "decrypt", 0, 1, "decrypt data"),
+ symmetric = arg_litn("s", "symmetric", 0, 1, "symmetric encryption with password"),
+ seckey = arg_filen("k", "seckey", "secretkey", 0, 1, "secret key file"),
+ pubkey = arg_filen("p", "pubkey", "publickey", 0, 1, "public key iles"),
+ taglen = arg_intn("t", "taglen", "<64,128,192,256>", 0, 1, "authentication message tag length in bits (default: 256)"),
+ noncelen = arg_intn("n", "noncelen", "<n>", 0, 1, "random nonce length in bytes (default: 32, must be >= 16)"),
+ cipherlen = arg_intn("c", "cipherlen", "<n>", 0, 1, "set random nonce length for <n> bytes output ciphertext size"),
+ base64 = arg_litn(NULL, "base64", 0, 1, "use base64 format without transformation"),
+ test = arg_litn(NULL, "test", 0, 1, "test crypto and encoding internal functions"),
+ nopassword = arg_litn(NULL, "no-password", 0, 1, "generate secret key without password"),
+ weak = arg_litn("w", "weak", 0, 1, "use weak length for nonce and auth tag (-n 4 -t 32)"),
+ infile = arg_filen("i", "input-file", "<infile>", 0, 1, "input file (default: stdin)"),
+ outfile = arg_filen("o", "output-file", "<outfile>", 0, 1, "output file (default: stdout)"),
+ end = arg_end(20),
+ };
+
+ int exitcode = 0;
+ const char progname[] = "inexact";
+ const char ver[] = "beta 1.0";
+ FILE *fo = NULL;
+
+ int nerrors;
+ nerrors = arg_parse(argc, argv, argtable);
+
+ /* special case: '--help' takes precedence over error reporting */
+ if (help->count > 0) {
+ printf("Usage: %s", progname);
+ arg_print_syntax(stdout, argtable, "\n");
+ printf(
+ "INadvisable EXperimental Asymmetric Crypto Tool, by "
+ "<ben@hackade.org>.\n\n");
+ arg_print_glossary(stdout, argtable, " %-25s %s\n");
+ exitcode = 0;
+ goto exit;
+ }
+
+ /* If the parser returned any errors then display them and exit */
+ if (nerrors > 0) {
+ /* Display the error details contained in the arg_end struct.*/
+ arg_print_errors(stdout, end, progname);
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+
+ /* check if an action is specified */
+ int action_count = gen->count + ddecrypt->count + dencrypt->count +
+ version->count + help->count + test->count;
+ if (action_count == 0) {
+ printf("Missing parameters.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+
+ if (version->count == 1) {
+ printf("version: %s\n", ver);
+ exitcode = 0;
+ goto exit;
+ }
+
+ if (test->count > 0) {
+ exitcode = test_all();
+ goto exit;
+ }
+
+ /* check if more than one action is specified */
+ if (action_count > 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+
+ if (symmetric->count == 1) {
+ if (gen->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (pubkey->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (seckey->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (nopassword->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ }
+
+ /* check if a secret key is specified */
+ if (seckey->count == 0 && symmetric->count == 0) {
+ printf("Missing secret key file operand.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+
+ /* check if public key is specified */
+ if (pubkey->count == 0 && symmetric->count == 0) {
+ printf("Missing public key file operand.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+
+ int nopassword_flag = (nopassword->count == 1);
+
+ /* generate action */
+ if (gen->count == 1) {
+ if (base64->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (infile->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (outfile->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (taglen->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (noncelen->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (access(seckey->filename[0], F_OK) != -1) {
+ char ch;
+ printf("Overwrite '%s' ? ", seckey->filename[0]);
+ int res = scanf("%c", &ch);
+ if ((ch != 'Y' && ch != 'y') || res == 0) {
+ exitcode = 1;
+ goto exit;
+ }
+ }
+ if (access(pubkey->filename[0], F_OK) != -1) {
+ char ch;
+ printf("Overwrite '%s' ? ", pubkey->filename[0]);
+ int res = scanf(" %c", &ch);
+ if ((ch != 'Y' && ch != 'y') || res == 0) {
+ exitcode = 1;
+ goto exit;
+ }
+ }
+
+ return generate_keys(seckey->filename[0], pubkey->filename[0],
+ nopassword_flag);
+ }
+
+ /* read input infile data */
+ unsigned char *data = NULL;
+ int data_len = 0;
+
+ if (infile->count == 0) {
+ /* read stdin */
+ int cap = 4096, len = 0;
+ data = malloc(cap * sizeof(unsigned char));
+ if (data == NULL) {
+ printf("malloc %d bytes failed.\n", cap);
+ exitcode = 1;
+ goto exit;
+ }
+ int c = 0;
+ do {
+ c = fgetc(stdin);
+ data[len] = c;
+ if (++len == cap) {
+ cap *= 2;
+ data = realloc(data, cap * sizeof(unsigned char));
+ if (data == NULL) {
+ printf("realloc %d bytes failed.\n", cap);
+ exitcode = 1;
+ goto exit;
+ }
+ }
+ } while (!feof(stdin));
+ fclose(stdin);
+ data = realloc(data, len * sizeof(unsigned char));
+ if (data == NULL) {
+ printf("realloc %d bytes failed.\n", cap);
+ exitcode = 1;
+ goto exit;
+ }
+ data[len - 1] = '\0';
+ data_len = len - 1;
+ } else if (access(infile->filename[0], F_OK) == -1) {
+ printf("Input file '%s' not found.\n", infile->filename[0]);
+ exitcode = 1;
+ goto exit;
+ } else {
+ FILE *fi = fopen(infile->filename[0], "rb");
+ if (fi == NULL) {
+ printf("open file '%s' failed: %s.\n", infile->filename[0],
+ strerror(errno));
+ exitcode = 1;
+ goto exit;
+ }
+ int rsb = fseek(fi, 0L, SEEK_END);
+ if (rsb != 0) {
+ printf("seek to end file '%s' failed: %s.\n", infile->filename[0],
+ strerror(errno));
+ fclose(fi);
+ exitcode = 1;
+ goto exit;
+ }
+ data_len = ftell(fi);
+ if (data_len == -1) {
+ printf("tell file '%s' failed: %s.\n", infile->filename[0],
+ strerror(errno));
+ fclose(fi);
+ exitcode = 1;
+ goto exit;
+ }
+ int rse = fseek(fi, 0L, SEEK_SET);
+ if (rse != 0) {
+ printf("seek file to begin'%s' failed: %s.\n", infile->filename[0],
+ strerror(errno));
+ fclose(fi);
+ exitcode = 1;
+ goto exit;
+ }
+ data = malloc(data_len);
+ if (data == NULL) {
+ printf("malloc %d bytes failed.\n", data_len);
+ fclose(fi);
+ exitcode = 1;
+ goto exit;
+ }
+ size_t readed = fread(data, 1, data_len, fi);
+ if (readed != data_len) {
+ printf("read file '%s' failed: %s.\n", infile->filename[0],
+ strerror(errno));
+ fclose(fi);
+ exitcode = 1;
+ goto exit;
+ }
+ fclose(fi);
+ }
+
+ /* check size limitation for input data (32 bits) */
+ if (data_len > 0xbfffffff) {
+ printf("Size of data too big.\n");
+ exitcode = 1;
+ goto exit;
+ }
+
+ int auth_tag_len = 32;
+ if (taglen->count == 1) {
+ if (ddecrypt->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (dencrypt->count == 0) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ int tag_len_value = taglen->ival[0];
+ if (tag_len_value != 64 && tag_len_value != 128 &&
+ tag_len_value != 192 && tag_len_value != 256) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ } else {
+ auth_tag_len = tag_len_value / 8;
+ }
+ }
+
+ /* output file */
+ if (outfile->count == 0) {
+ fo = stdout;
+ } else {
+ fo = fopen(outfile->filename[0], "wb");
+ if (fo == NULL) {
+ printf("open file '%s' failed: %s.\n", outfile->filename[0],
+ strerror(errno));
+ exitcode = 1;
+ goto exit;
+ }
+ }
+
+ const int rand_nonce_len_min = 16;
+ int rand_nonce_len = 32;
+ if (noncelen->count == 1) {
+ if (ddecrypt->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (dencrypt->count == 0) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (cipherlen->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ int noncelen_value = noncelen->ival[0];
+ if (noncelen_value < rand_nonce_len_min) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ } else {
+ rand_nonce_len = noncelen_value;
+ }
+ }
+
+ int base64_transformation = (base64->count == 0);
+ if (cipherlen->count == 1) {
+ if (dencrypt->count == 0 || noncelen->count == 1 || base64->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ size_t cipherlen_b64_value = cipherlen->ival[0];
+ size_t cipherlen_value = cipherlen_b64_value * 3 / 4;
+
+ size_t part0_len = 9;
+ size_t part1_len = data_len + auth_tag_len + rand_nonce_len;
+ size_t total_encrypted_len = part0_len + part1_len;
+ size_t total_encrypted_len_without_rand1 =
+ total_encrypted_len - rand_nonce_len;
+
+ if (symmetric->count == 1) {
+ total_encrypted_len_without_rand1 =
+ total_encrypted_len_without_rand1 + 64;
+ }
+
+ size_t rand_nonce_len_needed =
+ cipherlen_value - total_encrypted_len_without_rand1;
+
+ if (total_encrypted_len_without_rand1 > cipherlen_value ||
+ rand_nonce_len_needed < rand_nonce_len_min) {
+ unsigned long total_cipherlen_b64_min =
+ (total_encrypted_len_without_rand1 + rand_nonce_len_min) * 4 /
+ 3;
+ printf("Insufficient nonce length, cipherlen must be > %lu\n",
+ total_cipherlen_b64_min);
+ exitcode = 2;
+ goto exit;
+ } else {
+ rand_nonce_len = rand_nonce_len_needed;
+ }
+ }
+
+ if (weak->count == 1) {
+ if (dencrypt->count == 0 || noncelen->count == 1 || taglen->count == 1 ||
+ cipherlen->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ rand_nonce_len = 4;
+ auth_tag_len = 4;
+ }
+
+ if (dencrypt->count == 1) {
+ unsigned char secretkey[32] = {0};
+ unsigned char publickey[32] = {0};
+ unsigned char salt[32] = {0};
+ unsigned char *psalt = NULL;
+
+ if (symmetric->count == 1) {
+ if (get_symmetrickeys(salt, secretkey, publickey) != 0) {
+ printf("Symmetric keys generation failed.\n");
+ exitcode = 1;
+ goto exit;
+ }
+ psalt = &salt[0];
+ } else {
+ if (access(seckey->filename[0], F_OK) == -1) {
+ printf("Secret key file '%s' not found.\n",
+ seckey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ if (get_seckey(seckey->filename[0], secretkey, NULL) == 1) {
+ printf("File '%s': invalid key.\n", seckey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ if (access(pubkey->filename[0], F_OK) == -1) {
+ printf("Public key file '%s' not found.\n",
+ pubkey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ if (get_pubkey(pubkey->filename[0], publickey) == 1) {
+ printf("File '%s': invalid key.\n", pubkey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ }
+ size_t encrypted_len = 0;
+ unsigned char *encrypted = encrypt_data(
+ secretkey, publickey, psalt, data, data_len, rand_nonce_len,
+ auth_tag_len, base64_transformation, &encrypted_len);
+ if (encrypted == NULL) {
+ printf("Encryption failed.\n");
+ exitcode = 1;
+ goto exit;
+ }
+ fwrite(encrypted, 1, encrypted_len, fo);
+ if (base64_transformation) {
+ fwrite("\n", 1, 1, fo);
+ }
+ fflush(fo);
+ memset(encrypted, 0, encrypted_len);
+ memset(secretkey, 0, 32);
+ memset(publickey, 0, 32);
+ memset(data, 0, data_len);
+ free(encrypted);
+ }
+
+ if (ddecrypt->count == 1) {
+ unsigned char secretkey[32] = {0};
+ unsigned char publickey[32] = {0};
+
+ if (base64->count == 1) {
+ printf("Invalid options.\n");
+ printf("Try '%s --help' for more information.\n", progname);
+ exitcode = 1;
+ goto exit;
+ }
+ if (symmetric->count == 1) {
+ if (check_get_symmetrickeys(data, data_len, secretkey, publickey) !=
+ 0) {
+ exitcode = 1;
+ goto exit;
+ }
+ } else {
+ if (access(seckey->filename[0], F_OK) == -1) {
+ printf("Secret key file '%s' not found.\n",
+ seckey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ if (get_seckey(seckey->filename[0], secretkey, NULL) == 1) {
+ printf("File '%s': invalid key.\n", seckey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ if (access(pubkey->filename[0], F_OK) == -1) {
+ printf("Public key file '%s' not found.\n",
+ pubkey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ if (get_pubkey(pubkey->filename[0], publickey) == 1) {
+ printf("File '%s': invalid key.\n", pubkey->filename[0]);
+ exitcode = 1;
+ goto exit;
+ }
+ }
+
+ size_t decrypted_len = 0;
+ unsigned char *decrypted =
+ decrypt_data(secretkey, publickey, data, data_len,
+ (symmetric->count == 1), &decrypted_len);
+
+ if (decrypted == NULL || decrypted_len == 0) {
+ printf("Decryption failed.\n");
+ exitcode = 1;
+ goto exit;
+ }
+ fwrite(decrypted, 1, decrypted_len, fo);
+ fflush(fo);
+ memset(decrypted, 0, decrypted_len);
+ memset(secretkey, 0, 32);
+ memset(publickey, 0, 32);
+ memset(data, 0, data_len);
+ free(decrypted);
+ }
+
+exit:
+ if (fo != NULL) fclose(fo);
+
+ /* deallocate each non-null entry in argtable[] */
+ arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
+ return exitcode;
+}
diff --git a/src/norx_inexact.c b/src/norx_inexact.c
new file mode 100644
index 0000000..613d909
--- /dev/null
+++ b/src/norx_inexact.c
@@ -0,0 +1,701 @@
+/*
+ * NORX reference source code package - reference C implementations
+ *
+ * Written 2014-2016 by:
+ *
+ * - Samuel Neves <sneves@dei.uc.pt>
+ * - Philipp Jovanovic <philipp@jovanovic.io>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "norx_inexact_util.h"
+#include "norx_inexact.h"
+
+const char * norx_version = "3.0";
+
+#define NORX_N (NORX_W * 4) /* Nonce size */
+#define NORX_K (NORX_W * 4) /* Key size */
+#define NORX_B (NORX_W * 16) /* Permutation width */
+#define NORX_C (NORX_W * 4) /* Capacity */
+#define NORX_R (NORX_B - NORX_C) /* Rate */
+
+#if NORX_W == 32 /* NORX32 specific */
+
+ #define LOAD load32
+ #define STORE store32
+
+ /* Rotation constants */
+ #define R0 8
+ #define R1 11
+ #define R2 16
+ #define R3 31
+
+#elif NORX_W == 64 /* NORX64 specific */
+
+ #define LOAD load64
+ #define STORE store64
+
+ /* Rotation constants */
+ #define R0 8
+ #define R1 19
+ #define R2 40
+ #define R3 63
+
+#else
+ #error "Invalid word size!"
+#endif
+
+#if defined(NORX_DEBUG)
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#if NORX_W == 32
+ #define NORX_FMT "08" PRIX32
+#elif NORX_W == 64
+ #define NORX_FMT "016" PRIX64
+#endif
+
+static void norx_print_state(norx_state_t state)
+{
+ static const char fmt[] = "%" NORX_FMT " "
+ "%" NORX_FMT " "
+ "%" NORX_FMT " "
+ "%" NORX_FMT "\n";
+ const norx_word_t * S = state->S;
+ printf(fmt, S[ 0],S[ 1],S[ 2],S[ 3]);
+ printf(fmt, S[ 4],S[ 5],S[ 6],S[ 7]);
+ printf(fmt, S[ 8],S[ 9],S[10],S[11]);
+ printf(fmt, S[12],S[13],S[14],S[15]);
+ printf("\n");
+}
+
+static void print_bytes(const uint8_t *in, size_t inlen)
+{
+ size_t i;
+ for (i = 0; i < inlen; ++i) {
+ printf("%02X%c", in[i], i%16 == 15 ? '\n' : ' ');
+ }
+ if (inlen%16 != 0) {
+ printf("\n");
+ }
+}
+
+static void norx_debug(norx_state_t state, const uint8_t *in, size_t inlen, const uint8_t *out, size_t outlen)
+{
+ if (in != NULL && inlen > 0) {
+ printf("In:\n");
+ print_bytes(in, inlen);
+ }
+ if (out != NULL && outlen > 0) {
+ printf("Out:\n");
+ print_bytes(out, outlen);
+ }
+ printf("State:\n");
+ norx_print_state(state);
+}
+
+#endif
+
+/* The nonlinear primitive */
+#define H(A, B) ( ( (A) ^ (B) ) ^ ( ( (A) & (B) ) << 1) )
+
+/* The quarter-round */
+#define G(A, B, C, D) \
+do \
+{ \
+ (A) = H(A, B); (D) ^= (A); (D) = ROTR((D), R0); \
+ (C) = H(C, D); (B) ^= (C); (B) = ROTR((B), R1); \
+ (A) = H(A, B); (D) ^= (A); (D) = ROTR((D), R2); \
+ (C) = H(C, D); (B) ^= (C); (B) = ROTR((B), R3); \
+} while (0)
+
+/* The full round */
+static NORX_INLINE void F(norx_word_t S[16])
+{
+ /* Column step */
+ G(S[ 0], S[ 4], S[ 8], S[12]);
+ G(S[ 1], S[ 5], S[ 9], S[13]);
+ G(S[ 2], S[ 6], S[10], S[14]);
+ G(S[ 3], S[ 7], S[11], S[15]);
+ /* Diagonal step */
+ G(S[ 0], S[ 5], S[10], S[15]);
+ G(S[ 1], S[ 6], S[11], S[12]);
+ G(S[ 2], S[ 7], S[ 8], S[13]);
+ G(S[ 3], S[ 4], S[ 9], S[14]);
+}
+
+/* The core permutation */
+static NORX_INLINE void norx_permute(norx_state_t state)
+{
+ size_t i;
+ norx_word_t * S = state->S;
+
+ for (i = 0; i < NORX_L; ++i) {
+ F(S);
+ }
+}
+
+static NORX_INLINE void norx_pad(uint8_t *out, const uint8_t *in, const size_t inlen)
+{
+ memset(out, 0, BYTES(NORX_R));
+ memcpy(out, in, inlen);
+ out[inlen] = 0x01;
+ out[BYTES(NORX_R) - 1] |= 0x80;
+}
+
+static NORX_INLINE void norx_absorb_block(norx_state_t state, const uint8_t * in, tag_t tag)
+{
+ size_t i;
+ norx_word_t * S = state->S;
+
+ S[15] ^= tag;
+ norx_permute(state);
+
+ for (i = 0; i < WORDS(NORX_R); ++i) {
+ S[i] ^= LOAD(in + i * BYTES(NORX_W));
+ }
+}
+
+static NORX_INLINE void norx_absorb_lastblock(norx_state_t state, const uint8_t * in, size_t inlen, tag_t tag)
+{
+ uint8_t lastblock[BYTES(NORX_R)];
+ norx_pad(lastblock, in, inlen);
+ norx_absorb_block(state, lastblock, tag);
+}
+
+static NORX_INLINE void norx_encrypt_block(norx_state_t state, uint8_t *out, const uint8_t * in)
+{
+ size_t i;
+ norx_word_t * S = state->S;
+
+ S[15] ^= PAYLOAD_TAG;
+ norx_permute(state);
+
+ for (i = 0; i < WORDS(NORX_R); ++i) {
+ S[i] ^= LOAD(in + i * BYTES(NORX_W));
+ STORE(out + i * BYTES(NORX_W), S[i]);
+ }
+}
+
+static NORX_INLINE void norx_encrypt_lastblock(norx_state_t state, uint8_t *out, const uint8_t * in, size_t inlen)
+{
+ uint8_t lastblock[BYTES(NORX_R)];
+ norx_pad(lastblock, in, inlen);
+ norx_encrypt_block(state, lastblock, lastblock);
+ memcpy(out, lastblock, inlen);
+}
+
+static NORX_INLINE void norx_decrypt_block(norx_state_t state, uint8_t *out, const uint8_t * in)
+{
+ size_t i;
+ norx_word_t * S = state->S;
+
+ S[15] ^= PAYLOAD_TAG;
+ norx_permute(state);
+
+ for (i = 0; i < WORDS(NORX_R); ++i) {
+ const norx_word_t c = LOAD(in + i * BYTES(NORX_W));
+ STORE(out + i * BYTES(NORX_W), S[i] ^ c);
+ S[i] = c;
+ }
+}
+
+static NORX_INLINE void norx_decrypt_lastblock(norx_state_t state, uint8_t *out, const uint8_t * in, size_t inlen)
+{
+ norx_word_t * S = state->S;
+ uint8_t lastblock[BYTES(NORX_R)];
+ size_t i;
+
+ S[15] ^= PAYLOAD_TAG;
+ norx_permute(state);
+
+ for(i = 0; i < WORDS(NORX_R); ++i) {
+ STORE(lastblock + i * BYTES(NORX_W), S[i]);
+ }
+
+ memcpy(lastblock, in, inlen);
+ lastblock[inlen] ^= 0x01;
+ lastblock[BYTES(NORX_R) - 1] ^= 0x80;
+
+ for (i = 0; i < WORDS(NORX_R); ++i) {
+ const norx_word_t c = LOAD(lastblock + i * BYTES(NORX_W));
+ STORE(lastblock + i * BYTES(NORX_W), S[i] ^ c);
+ S[i] = c;
+ }
+
+ memcpy(out, lastblock, inlen);
+ burn(lastblock, 0, sizeof lastblock);
+}
+
+/* Low-level operations */
+static NORX_INLINE void norx_init(norx_state_t state, const unsigned char *k, const unsigned char *n, size_t tlen)
+{
+ norx_word_t * S = state->S;
+ size_t i;
+
+ for(i = 0; i < 16; ++i) {
+ S[i] = i;
+ }
+
+ F(S);
+ F(S);
+
+ S[ 0] = LOAD(n + 0 * BYTES(NORX_W));
+ S[ 1] = LOAD(n + 1 * BYTES(NORX_W));
+ S[ 2] = LOAD(n + 2 * BYTES(NORX_W));
+ S[ 3] = LOAD(n + 3 * BYTES(NORX_W));
+
+ S[ 4] = LOAD(k + 0 * BYTES(NORX_W));
+ S[ 5] = LOAD(k + 1 * BYTES(NORX_W));
+ S[ 6] = LOAD(k + 2 * BYTES(NORX_W));
+ S[ 7] = LOAD(k + 3 * BYTES(NORX_W));
+
+ S[12] ^= NORX_W;
+ S[13] ^= NORX_L;
+ S[14] ^= NORX_P;
+ S[15] ^= tlen;
+
+ norx_permute(state);
+
+ S[12] ^= LOAD(k + 0 * BYTES(NORX_W));
+ S[13] ^= LOAD(k + 1 * BYTES(NORX_W));
+ S[14] ^= LOAD(k + 2 * BYTES(NORX_W));
+ S[15] ^= LOAD(k + 3 * BYTES(NORX_W));
+
+#if defined(NORX_DEBUG)
+ printf("Initialise\n");
+ norx_debug(state, NULL, 0, NULL, 0);
+#endif
+}
+
+void norx_absorb_data(norx_state_t state, const unsigned char * in, size_t inlen, tag_t tag)
+{
+ if (inlen > 0)
+ {
+ while (inlen >= BYTES(NORX_R))
+ {
+ norx_absorb_block(state, in, tag);
+ #if defined(NORX_DEBUG)
+ printf("Absorb block\n");
+ norx_debug(state, in, BYTES(NORX_R), NULL, 0);
+ #endif
+ inlen -= BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ }
+ norx_absorb_lastblock(state, in, inlen, tag);
+ #if defined(NORX_DEBUG)
+ printf("Absorb lastblock\n");
+ norx_debug(state, in, inlen, NULL, 0);
+ #endif
+ }
+}
+
+#if NORX_P != 1 /* only required in parallel modes */
+static NORX_INLINE void norx_branch(norx_state_t state, uint64_t lane)
+{
+ size_t i;
+ norx_word_t * S = state->S;
+
+ S[15] ^= BRANCH_TAG;
+ norx_permute(state);
+
+ /* Inject lane ID */
+ for (i = 0; i < WORDS(NORX_R); ++i) {
+ S[i] ^= lane;
+ }
+}
+
+/* state = state xor state1 */
+static NORX_INLINE void norx_merge(norx_state_t state, norx_state_t state1)
+{
+ size_t i;
+ norx_word_t * S = state->S;
+ norx_word_t * S1 = state1->S;
+
+ S1[15] ^= MERGE_TAG;
+ norx_permute(state1);
+
+ for (i = 0; i < 16; ++i) {
+ S[i] ^= S1[i];
+ }
+}
+#endif
+
+#if NORX_P == 1 /* Sequential encryption/decryption */
+void norx_encrypt_data(norx_state_t state, unsigned char *out, const unsigned char * in, size_t inlen)
+{
+ if (inlen > 0)
+ {
+ while (inlen >= BYTES(NORX_R))
+ {
+ norx_encrypt_block(state, out, in);
+ #if defined(NORX_DEBUG)
+ printf("Encrypt block\n");
+ norx_debug(state, in, BYTES(NORX_R), out, BYTES(NORX_R));
+ #endif
+ inlen -= BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ out += BYTES(NORX_R);
+ }
+ norx_encrypt_lastblock(state, out, in, inlen);
+ #if defined(NORX_DEBUG)
+ printf("Encrypt lastblock\n");
+ norx_debug(state, in, inlen, out, inlen);
+ #endif
+ }
+}
+
+void norx_decrypt_data(norx_state_t state, unsigned char *out, const unsigned char * in, size_t inlen)
+{
+ if (inlen > 0)
+ {
+ while (inlen >= BYTES(NORX_R))
+ {
+ norx_decrypt_block(state, out, in);
+ #if defined(NORX_DEBUG)
+ printf("Decrypt block\n");
+ norx_debug(state, in, BYTES(NORX_R), out, BYTES(NORX_R));
+ #endif
+ inlen -= BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ out += BYTES(NORX_R);
+ }
+ norx_decrypt_lastblock(state, out, in, inlen);
+ #if defined(NORX_DEBUG)
+ printf("Decrypt lastblock\n");
+ norx_debug(state, in, inlen, out, inlen);
+ #endif
+ }
+}
+
+#elif NORX_P > 1 /* Parallel encryption/decryption */
+void norx_encrypt_data(norx_state_t state, unsigned char *out, const unsigned char * in, size_t inlen)
+{
+ if (inlen > 0)
+ {
+ size_t i;
+ norx_state_t lane[NORX_P];
+
+ /* Initialize states + branch */
+ for (i = 0; i < NORX_P; ++i) {
+ memcpy(lane[i], state, sizeof lane[i]);
+ norx_branch(lane[i], i);
+ }
+
+ /* Parallel payload processing */
+ for (i = 0; inlen >= BYTES(NORX_R); ++i) {
+ norx_encrypt_block(lane[i%NORX_P], out, in);
+ #if defined(NORX_DEBUG)
+ printf("Encrypt block (lane: %lu)\n", i%NORX_P);
+ norx_debug(lane[i%NORX_P], in, BYTES(NORX_R), out, BYTES(NORX_R));
+ #endif
+ inlen -= BYTES(NORX_R);
+ out += BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ }
+ norx_encrypt_lastblock(lane[i%NORX_P], out, in, inlen);
+ #if defined(NORX_DEBUG)
+ printf("Encrypt lastblock (lane: %lu)\n", i%NORX_P);
+ norx_debug(lane[i%NORX_P], in, inlen, out, inlen);
+ #endif
+
+ /* Merge */
+ memset(state, 0, sizeof(norx_state_t));
+ for (i = 0; i < NORX_P; ++i) {
+ norx_merge(state, lane[i]);
+ burn(lane[i], 0, sizeof(norx_state_t));
+ }
+
+ #if defined(NORX_DEBUG)
+ printf("Encryption finalised\n");
+ norx_debug(state, NULL, 0, NULL, 0);
+ #endif
+ }
+}
+
+void norx_decrypt_data(norx_state_t state, unsigned char *out, const unsigned char * in, size_t inlen)
+{
+ if (inlen > 0)
+ {
+ size_t i;
+ norx_state_t lane[NORX_P];
+
+ /* Initialize states + branch */
+ for (i = 0; i < NORX_P; ++i) {
+ memcpy(lane[i], state, sizeof lane[i]);
+ norx_branch(lane[i], i);
+ }
+
+ /* Parallel payload processing */
+ for (i = 0; inlen >= BYTES(NORX_R); ++i) {
+ norx_decrypt_block(lane[i%NORX_P], out, in);
+ #if defined(NORX_DEBUG)
+ printf("Decrypt block (lane: %lu)\n", i%NORX_P);
+ norx_debug(lane[i%NORX_P], in, BYTES(NORX_R), out, BYTES(NORX_R));
+ #endif
+ inlen -= BYTES(NORX_R);
+ out += BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ }
+ norx_decrypt_lastblock(lane[i%NORX_P], out, in, inlen);
+ #if defined(NORX_DEBUG)
+ printf("Decrypt lastblock (lane: %lu)\n", i%NORX_P);
+ norx_debug(lane[i%NORX_P], in, inlen, out, inlen);
+ #endif
+
+ /* Merge */
+ memset(state, 0, sizeof(norx_state_t));
+ for (i = 0; i < NORX_P; ++i) {
+ norx_merge(state, lane[i]);
+ burn(lane[i], 0, sizeof(norx_state_t));
+ }
+
+ #if defined(NORX_DEBUG)
+ printf("Decryption finalised\n");
+ norx_debug(state, NULL, 0, NULL, 0);
+ #endif
+ }
+}
+
+#elif NORX_P == 0 /* Unlimited parallelism */
+void norx_encrypt_data(norx_state_t state, unsigned char *out, const unsigned char * in, size_t inlen)
+{
+ if (inlen > 0)
+ {
+ size_t lane = 0;
+ norx_state_t sum;
+ norx_state_t state2;
+
+ memset(sum, 0, sizeof(norx_state_t));
+
+ while (inlen >= BYTES(NORX_R))
+ {
+ /* branch */
+ memcpy(state2, state, sizeof(norx_state_t));
+ norx_branch(state2, lane++);
+ /* encrypt */
+ norx_encrypt_block(state2, out, in);
+
+ #if defined(NORX_DEBUG)
+ printf("Encrypt block (lane: %lu)\n", lane - 1);
+ norx_debug(state2, in, BYTES(NORX_R), out, BYTES(NORX_R));
+ #endif
+
+ /* merge */
+ norx_merge(sum, state2);
+
+ inlen -= BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ out += BYTES(NORX_R);
+ }
+
+ /* last block, 0 <= inlen < BYTES(NORX_R) */
+
+ /* branch */
+ memcpy(state2, state, sizeof(norx_state_t));
+ norx_branch(state2, lane++);
+
+ /* encrypt */
+ norx_encrypt_lastblock(state2, out, in, inlen);
+
+ #if defined(NORX_DEBUG)
+ printf("Encrypt lastblock (lane: %lu)\n", lane - 1);
+ norx_debug(state2, in, inlen, out, inlen);
+ #endif
+
+ /* merge */
+ norx_merge(sum, state2);
+
+ memcpy(state, sum, sizeof(norx_state_t));
+ burn(state2, 0, sizeof(norx_state_t));
+ burn(sum, 0, sizeof(norx_state_t));
+
+ #if defined(NORX_DEBUG)
+ printf("Encryption finalised\n");
+ norx_debug(state, NULL, 0, NULL, 0);
+ #endif
+ }
+}
+
+void norx_decrypt_data(norx_state_t state, unsigned char *out, const unsigned char * in, size_t inlen)
+{
+ if (inlen > 0)
+ {
+ size_t lane = 0;
+ norx_state_t sum;
+ norx_state_t state2;
+
+ memset(sum, 0, sizeof(norx_state_t));
+
+ while (inlen >= BYTES(NORX_R))
+ {
+ /* branch */
+ memcpy(state2, state, sizeof(norx_state_t));
+ norx_branch(state2, lane++);
+ /* decrypt */
+ norx_decrypt_block(state2, out, in);
+
+ #if defined(NORX_DEBUG)
+ printf("Decrypt block (lane: %lu)\n", lane - 1);
+ norx_debug(state2, in, BYTES(NORX_R), out, BYTES(NORX_R));
+ #endif
+
+ /* merge */
+ norx_merge(sum, state2);
+
+ inlen -= BYTES(NORX_R);
+ in += BYTES(NORX_R);
+ out += BYTES(NORX_R);
+ }
+
+ /* last block, 0 <= inlen < BYTES(NORX_R) */
+
+ /* branch */
+ memcpy(state2, state, sizeof(norx_state_t));
+ norx_branch(state2, lane++);
+
+ /* decrypt */
+ norx_decrypt_lastblock(state2, out, in, inlen);
+
+ #if defined(NORX_DEBUG)
+ printf("Decrypt lastblock (lane: %lu)\n", lane - 1);
+ norx_debug(state2, in, inlen, out, inlen);
+ #endif
+
+ /* merge */
+ norx_merge(sum, state2);
+
+ memcpy(state, sum, sizeof(norx_state_t));
+ burn(state2, 0, sizeof(norx_state_t));
+ burn(sum, 0, sizeof(norx_state_t));
+
+ #if defined(NORX_DEBUG)
+ printf("Decryption finalised\n");
+ norx_debug(state, NULL, 0, NULL, 0);
+ #endif
+ }
+}
+#else /* D < 0 */
+ #error "NORX_P cannot be < 0"
+#endif
+
+static NORX_INLINE void norx_finalise(norx_state_t state, unsigned char * tag, const unsigned char * k, int tlen)
+{
+ norx_word_t * S = state->S;
+ uint8_t lastblock[BYTES(NORX_C)];
+
+ S[15] ^= FINAL_TAG;
+
+ norx_permute(state);
+
+ S[12] ^= LOAD(k + 0 * BYTES(NORX_W));
+ S[13] ^= LOAD(k + 1 * BYTES(NORX_W));
+ S[14] ^= LOAD(k + 2 * BYTES(NORX_W));
+ S[15] ^= LOAD(k + 3 * BYTES(NORX_W));
+
+ norx_permute(state);
+
+ S[12] ^= LOAD(k + 0 * BYTES(NORX_W));
+ S[13] ^= LOAD(k + 1 * BYTES(NORX_W));
+ S[14] ^= LOAD(k + 2 * BYTES(NORX_W));
+ S[15] ^= LOAD(k + 3 * BYTES(NORX_W));
+
+ STORE(lastblock + 0 * BYTES(NORX_W), S[12]);
+ STORE(lastblock + 1 * BYTES(NORX_W), S[13]);
+ STORE(lastblock + 2 * BYTES(NORX_W), S[14]);
+ STORE(lastblock + 3 * BYTES(NORX_W), S[15]);
+
+ memcpy(tag, lastblock, BYTES(tlen));
+
+ #if defined(NORX_DEBUG)
+ printf("Finalise\n");
+ norx_debug(state, NULL, 0, NULL, 0);
+ #endif
+
+ burn(lastblock, 0, BYTES(NORX_C)); /* burn buffer */
+ burn(state, 0, sizeof(norx_state_t)); /* at this point we can also burn the state */
+}
+
+/* Verify tags in constant time: 0 for success, -1 for fail */
+int norx_verify_tag(const unsigned char * tag1, const unsigned char * tag2, int tlen)
+{
+ size_t i;
+ unsigned acc = 0;
+
+ for (i = 0; i < BYTES(tlen); ++i) {
+ acc |= tag1[i] ^ tag2[i];
+ }
+
+ return (((acc - 1) >> 8) & 1) - 1;
+}
+
+/* High-level operations */
+void norx_aead_encrypt(
+ unsigned char *c, size_t *clen,
+ const unsigned char *a, size_t alen,
+ const unsigned char *m, size_t mlen,
+ const unsigned char *z, size_t zlen,
+ const unsigned char *nonce,
+ const unsigned char *key,
+ size_t tlen
+)
+{
+ unsigned char k[BYTES(NORX_K)];
+ norx_state_t state;
+
+ memcpy(k, key, sizeof(k));
+ norx_init(state, k, nonce, tlen);
+ norx_absorb_data(state, a, alen, HEADER_TAG);
+ norx_encrypt_data(state, c, m, mlen);
+ norx_absorb_data(state, z, zlen, TRAILER_TAG);
+ norx_finalise(state, c + mlen, k, tlen);
+ *clen = mlen + BYTES(tlen);
+
+ burn(state, 0, sizeof(norx_state_t));
+ burn(k, 0, sizeof(k));
+}
+
+int norx_aead_decrypt(
+ unsigned char *m, size_t *mlen,
+ const unsigned char *a, size_t alen,
+ const unsigned char *c, size_t clen,
+ const unsigned char *z, size_t zlen,
+ const unsigned char *nonce,
+ const unsigned char *key,
+ size_t tlen
+)
+{
+ unsigned char k[BYTES(NORX_K)];
+ unsigned char tag[BYTES(tlen)];
+ norx_state_t state;
+ int result = -1;
+
+ if (clen < BYTES(tlen)) {
+ return -1;
+ }
+
+ memcpy(k, key, sizeof(k));
+ norx_init(state, k, nonce, tlen);
+ norx_absorb_data(state, a, alen, HEADER_TAG);
+ norx_decrypt_data(state, m, c, clen - BYTES(tlen));
+ norx_absorb_data(state, z, zlen, TRAILER_TAG);
+ norx_finalise(state, tag, k, tlen);
+ *mlen = clen - BYTES(tlen);
+
+ result = norx_verify_tag(c + clen - BYTES(tlen), tag, tlen);
+ if (result != 0) { /* burn decrypted plaintext on auth failure */
+ burn(m, 0, clen - BYTES(tlen));
+ }
+ burn(state, 0, sizeof(norx_state_t));
+ burn(k, 0, sizeof(k));
+ return result;
+}
diff --git a/src/norx_inexact.h b/src/norx_inexact.h
new file mode 100644
index 0000000..ec35d73
--- /dev/null
+++ b/src/norx_inexact.h
@@ -0,0 +1,64 @@
+/*
+ * NORX reference source code package - reference C implementations
+ *
+ * Written 2014-2016 by:
+ *
+ * - Samuel Neves <sneves@dei.uc.pt>
+ * - Philipp Jovanovic <philipp@jovanovic.io>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+#ifndef NORX_NORX_H
+#define NORX_NORX_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "norx_inexact_config.h"
+
+#if NORX_W == 64
+ typedef uint64_t norx_word_t;
+#elif NORX_W == 32
+ typedef uint32_t norx_word_t;
+#else
+ #error "Invalid word size!"
+#endif
+
+typedef struct norx_state__
+{
+ norx_word_t S[16];
+} norx_state_t[1];
+
+typedef enum tag__
+{
+ HEADER_TAG = 0x01,
+ PAYLOAD_TAG = 0x02,
+ TRAILER_TAG = 0x04,
+ FINAL_TAG = 0x08,
+ BRANCH_TAG = 0x10,
+ MERGE_TAG = 0x20
+} tag_t;
+
+/* High-level operations */
+void norx_aead_encrypt(
+ unsigned char *c, size_t *clen,
+ const unsigned char *a, size_t alen,
+ const unsigned char *m, size_t mlen,
+ const unsigned char *z, size_t zlen,
+ const unsigned char *nonce,
+ const unsigned char *key,
+ size_t tlen);
+
+int norx_aead_decrypt(
+ unsigned char *m, size_t *mlen,
+ const unsigned char *a, size_t alen,
+ const unsigned char *c, size_t clen,
+ const unsigned char *z, size_t zlen,
+ const unsigned char *nonce,
+ const unsigned char *key,
+ size_t tlen);
+#endif
diff --git a/src/norx_inexact_config.h b/src/norx_inexact_config.h
new file mode 100644
index 0000000..6e42730
--- /dev/null
+++ b/src/norx_inexact_config.h
@@ -0,0 +1,20 @@
+/*
+ * NORX reference source code package - reference C implementations
+ *
+ * Written 2014-2016 by:
+ *
+ * - Samuel Neves <sneves@dei.uc.pt>
+ * - Philipp Jovanovic <philipp@jovanovic.io>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+#define NORX_W 64 /* Word size */
+#define NORX_L 6 /* Round number */
+#define NORX_P 1 /* Parallelism degree */
+#define NORX_TT (NORX_W * 4) /* Tag size */
+
diff --git a/src/norx_inexact_util.h b/src/norx_inexact_util.h
new file mode 100644
index 0000000..e490372
--- /dev/null
+++ b/src/norx_inexact_util.h
@@ -0,0 +1,119 @@
+/*
+ * NORX reference source code package - reference C implementations
+ *
+ * Written 2014-2016 by:
+ *
+ * - Samuel Neves <sneves@dei.uc.pt>
+ * - Philipp Jovanovic <philipp@jovanovic.io>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+#ifndef NORX_DEFS_H
+#define NORX_DEFS_H
+
+/* Workaround for C89 compilers */
+#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
+ #if defined(_MSC_VER)
+ #define NORX_INLINE __inline
+ #elif defined(__GNUC__)
+ #define NORX_INLINE __inline__
+ #else
+ #define NORX_INLINE
+ #endif
+#else
+ #define NORX_INLINE inline
+#endif
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdint.h>
+
+#define STR_(x) #x
+#define STR(x) STR_(x)
+#define PASTE_(A, B, C) A ## B ## C
+#define PASTE(A, B, C) PASTE_(A, B, C)
+#define BYTES(x) (((x) + 7) / 8)
+#define WORDS(x) (((x) + (NORX_W-1)) / NORX_W)
+
+#define BITS(x) (sizeof(x) * CHAR_BIT)
+#define ROTL(x, c) ( ((x) << (c)) | ((x) >> (BITS(x) - (c))) )
+#define ROTR(x, c) ( ((x) >> (c)) | ((x) << (BITS(x) - (c))) )
+
+static NORX_INLINE uint32_t load32(const void * in)
+{
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ uint32_t v;
+ memcpy(&v, in, sizeof v);
+ return v;
+#else
+ const uint8_t * p = (const uint8_t *)in;
+ return ((uint32_t)p[0] << 0) |
+ ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16) |
+ ((uint32_t)p[3] << 24);
+#endif
+}
+
+
+static NORX_INLINE uint64_t load64(const void * in)
+{
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ uint64_t v;
+ memcpy(&v, in, sizeof v);
+ return v;
+#else
+ const uint8_t * p = (const uint8_t *)in;
+ return ((uint64_t)p[0] << 0) |
+ ((uint64_t)p[1] << 8) |
+ ((uint64_t)p[2] << 16) |
+ ((uint64_t)p[3] << 24) |
+ ((uint64_t)p[4] << 32) |
+ ((uint64_t)p[5] << 40) |
+ ((uint64_t)p[6] << 48) |
+ ((uint64_t)p[7] << 56);
+#endif
+}
+
+
+static NORX_INLINE void store32(void * out, const uint32_t v)
+{
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ memcpy(out, &v, sizeof v);
+#else
+ uint8_t * p = (uint8_t *)out;
+ p[0] = (uint8_t)(v >> 0);
+ p[1] = (uint8_t)(v >> 8);
+ p[2] = (uint8_t)(v >> 16);
+ p[3] = (uint8_t)(v >> 24);
+#endif
+}
+
+
+static NORX_INLINE void store64(void * out, const uint64_t v)
+{
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ memcpy(out, &v, sizeof v);
+#else
+ uint8_t * p = (uint8_t *)out;
+ p[0] = (uint8_t)(v >> 0);
+ p[1] = (uint8_t)(v >> 8);
+ p[2] = (uint8_t)(v >> 16);
+ p[3] = (uint8_t)(v >> 24);
+ p[4] = (uint8_t)(v >> 32);
+ p[5] = (uint8_t)(v >> 40);
+ p[6] = (uint8_t)(v >> 48);
+ p[7] = (uint8_t)(v >> 56);
+#endif
+}
+
+
+static void* (* const volatile burn)(void*, int, size_t) = memset;
+
+#endif
+
diff --git a/src/randombytes.c b/src/randombytes.c
new file mode 100644
index 0000000..105aa58
--- /dev/null
+++ b/src/randombytes.c
@@ -0,0 +1,262 @@
+// In the case that are compiling on linux, we need to define _GNU_SOURCE
+// *before* randombytes.h is included. Otherwise SYS_getrandom will not be
+// declared.
+#if defined(__linux__)
+# define _GNU_SOURCE
+#endif /* defined(__linux__) */
+
+#include "randombytes.h"
+
+#if defined(_WIN32)
+/* Windows */
+# include <windows.h>
+# include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */
+#endif /* defined(_WIN32) */
+
+
+#if defined(__linux__)
+/* Linux */
+// We would need to include <linux/random.h>, but not every target has access
+// to the linux headers. We only need RNDGETENTCNT, so we instead inline it.
+// RNDGETENTCNT is originally defined in `include/uapi/linux/random.h` in the
+// linux repo.
+# define RNDGETENTCNT 0x80045200
+
+# include <assert.h>
+# include <errno.h>
+# include <fcntl.h>
+# include <poll.h>
+# include <stdint.h>
+# include <sys/ioctl.h>
+# include <sys/stat.h>
+# include <sys/syscall.h>
+# include <sys/types.h>
+# include <unistd.h>
+
+// We need SSIZE_MAX as the maximum read len from /dev/urandom
+# if !defined(SSIZE_MAX)
+# define SSIZE_MAX (SIZE_MAX / 2 - 1)
+# endif /* defined(SSIZE_MAX) */
+
+#endif /* defined(__linux__) */
+
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+/* Dragonfly, FreeBSD, NetBSD, OpenBSD (has arc4random) */
+# include <sys/param.h>
+# if defined(BSD)
+# include <stdlib.h>
+# endif
+#endif
+
+#if defined(__EMSCRIPTEN__)
+# include <assert.h>
+# include <emscripten.h>
+# include <errno.h>
+# include <stdbool.h>
+#endif /* defined(__EMSCRIPTEN__) */
+
+
+#if defined(_WIN32)
+static int randombytes_win32_randombytes(void* buf, const size_t n)
+{
+ HCRYPTPROV ctx;
+ BOOL tmp;
+
+ tmp = CryptAcquireContext(&ctx, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT);
+ if (tmp == FALSE) return -1;
+
+ tmp = CryptGenRandom(ctx, n, (BYTE*) buf);
+ if (tmp == FALSE) return -1;
+
+ tmp = CryptReleaseContext(ctx, 0);
+ if (tmp == FALSE) return -1;
+
+ return 0;
+}
+#endif /* defined(_WIN32) */
+
+
+#if defined(__linux__) && defined(SYS_getrandom)
+static int randombytes_linux_randombytes_getrandom(void *buf, size_t n)
+{
+ /* I have thought about using a separate PRF, seeded by getrandom, but
+ * it turns out that the performance of getrandom is good enough
+ * (250 MB/s on my laptop).
+ */
+ size_t offset = 0, chunk;
+ int ret;
+ while (n > 0) {
+ /* getrandom does not allow chunks larger than 33554431 */
+ chunk = n <= 33554431 ? n : 33554431;
+ do {
+ ret = syscall(SYS_getrandom, (char *)buf + offset, chunk, 0);
+ } while (ret == -1 && errno == EINTR);
+ if (ret < 0) return ret;
+ offset += ret;
+ n -= ret;
+ }
+ assert(n == 0);
+ return 0;
+}
+#endif /* defined(__linux__) && defined(SYS_getrandom) */
+
+
+#if defined(__linux__) && !defined(SYS_getrandom)
+static int randombytes_linux_wait_for_entropy(int device)
+{
+ /* We will block on /dev/random, because any increase in the OS' entropy
+ * level will unblock the request. I use poll here (as does libsodium),
+ * because we don't *actually* want to read from the device. */
+ const int bits = 128;
+ struct pollfd pfd;
+ int fd;
+ int retcode, retcode_error = 0; // Used as return codes throughout this function
+ int entropy = 0;
+
+ /* If the device has enough entropy already, we will want to return early */
+ retcode = ioctl(device, RNDGETENTCNT, &entropy);
+ if (retcode != 0) {
+ // Unrecoverable ioctl error
+ // TODO(dsprenkels) Use `/proc/sys/kernel/random/entropy_avail`
+ return retcode;
+ }
+ if (entropy >= bits) {
+ return 0;
+ }
+
+ do {
+ fd = open("/dev/random", O_RDONLY);
+ } while (fd == -1 && errno == EINTR); /* EAGAIN will not occur */
+ if (fd == -1) {
+ /* Unrecoverable IO error */
+ return -1;
+ }
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ for (;;) {
+ retcode = poll(&pfd, 1, -1);
+ if (retcode == -1 && (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ } else if (retcode == 1) {
+ retcode = ioctl(device, RNDGETENTCNT, &entropy);
+ if (retcode != 0) {
+ // Unrecoverable ioctl error
+ retcode_error = retcode;
+ break;
+ }
+ if (entropy >= bits) {
+ break;
+ }
+ } else {
+ // Unreachable: poll() can should only return -1 or 1
+ retcode_error = -1;
+ break;
+ }
+ }
+ do {
+ retcode = close(fd);
+ } while (retcode == -1 && errno == EINTR);
+ if (retcode_error != 0) {
+ return retcode_error;
+ }
+ return retcode;
+}
+
+
+static int randombytes_linux_randombytes_urandom(void *buf, size_t n)
+{
+ int fd;
+ size_t offset = 0, count;
+ ssize_t tmp;
+ do {
+ fd = open("/dev/urandom", O_RDONLY);
+ } while (fd == -1 && errno == EINTR);
+ if (fd == -1) return -1;
+ if (randombytes_linux_wait_for_entropy(fd) == -1) return -1;
+
+ while (n > 0) {
+ count = n <= SSIZE_MAX ? n : SSIZE_MAX;
+ tmp = read(fd, (char *)buf + offset, count);
+ if (tmp == -1 && (errno == EAGAIN || errno == EINTR)) {
+ continue;
+ }
+ if (tmp == -1) return -1; /* Unrecoverable IO error */
+ offset += tmp;
+ n -= tmp;
+ }
+ assert(n == 0);
+ return 0;
+}
+#endif /* defined(__linux__) && !defined(SYS_getrandom) */
+
+
+#if defined(BSD)
+static int randombytes_bsd_randombytes(void *buf, size_t n)
+{
+ arc4random_buf(buf, n);
+ return 0;
+}
+#endif /* defined(BSD) */
+
+
+#if defined(__EMSCRIPTEN__)
+static int randombytes_js_randombytes_nodejs(void *buf, size_t n) {
+ const int ret = EM_ASM_INT({
+ var crypto;
+ try {
+ crypto = require('crypto');
+ } catch (error) {
+ return -2;
+ }
+ try {
+ writeArrayToMemory(crypto.randomBytes($1), $0);
+ return 0;
+ } catch (error) {
+ return -1;
+ }
+ }, buf, n);
+ switch (ret) {
+ case 0:
+ return 0;
+ case -1:
+ errno = EINVAL;
+ return -1;
+ case -2:
+ errno = ENOSYS;
+ return -1;
+ }
+ assert(false); // Unreachable
+}
+#endif /* defined(__EMSCRIPTEN__) */
+
+
+int randombytes(void *buf, size_t n)
+{
+#if defined(__EMSCRIPTEN__)
+# pragma message("Using crypto api from NodeJS")
+ return randombytes_js_randombytes_nodejs(buf, n);
+#elif defined(__linux__)
+# if defined(SYS_getrandom)
+# pragma message("Using getrandom system call")
+ /* Use getrandom system call */
+ return randombytes_linux_randombytes_getrandom(buf, n);
+# else
+# pragma message("Using /dev/urandom device")
+ /* When we have enough entropy, we can read from /dev/urandom */
+ return randombytes_linux_randombytes_urandom(buf, n);
+# endif
+#elif defined(BSD)
+# pragma message("Using arc4random system call")
+ /* Use arc4random system call */
+ return randombytes_bsd_randombytes(buf, n);
+#elif defined(_WIN32)
+# pragma message("Using Windows cryptographic API")
+ /* Use windows API */
+ return randombytes_win32_randombytes(buf, n);
+#else
+# error "randombytes(...) is not supported on this platform"
+#endif
+}
diff --git a/src/randombytes.h b/src/randombytes.h
new file mode 100644
index 0000000..f5cc736
--- /dev/null
+++ b/src/randombytes.h
@@ -0,0 +1,19 @@
+#ifndef sss_RANDOMBYTES_H
+#define sss_RANDOMBYTES_H
+
+#ifdef _WIN32
+/* Load size_t on windows */
+//#include <CRTDEFS.H>
+#include <inttypes.h>
+#else
+#include <unistd.h>
+#endif /* _WIN32 */
+
+
+/*
+ * Write `n` bytes of high quality random bytes to `buf`
+ */
+int randombytes(void *buf, size_t n);
+
+
+#endif /* sss_RANDOMBYTES_H */
diff --git a/src/readpassphrase.c b/src/readpassphrase.c
new file mode 100644
index 0000000..64b4947
--- /dev/null
+++ b/src/readpassphrase.c
@@ -0,0 +1,283 @@
+#include "readpassphrase.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <stdio.h>
+#include <conio.h>
+
+/*on error returns NULL and sets errno*/
+wchar_t *
+utf8_to_utf16(const char *utf8)
+{
+ int needed = 0;
+ wchar_t* utf16 = NULL;
+ if ((needed = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) == 0 ||
+ (utf16 = malloc(needed * sizeof(wchar_t))) == NULL ||
+ MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, needed) == 0) {
+ printf("failed to convert utf8 payload:%s error:%li", utf8, GetLastError());
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ return utf16;
+}
+
+char *readpassphrase(const char *prompt, char *outBuf, size_t outBufLen, int flags)
+{
+ int current_index = 0;
+ char ch;
+ wchar_t* wtmp = NULL;
+
+ if (outBufLen == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ while (_kbhit()) _getch();
+
+ wtmp = utf8_to_utf16(prompt);
+ if (wtmp == NULL)
+ printf("unable to alloc memory");
+
+ _cputws(wtmp);
+ free(wtmp);
+
+ while (current_index < outBufLen - 1) {
+ ch = _getch();
+
+ if (ch == '\r') {
+ if (_kbhit()) _getch(); /* read linefeed if its there */
+ break;
+ } else if (ch == '\n') {
+ break;
+ } else if (ch == '\b') { /* backspace */
+ if (current_index > 0) {
+ if (flags & RPP_ECHO_ON)
+ printf_s("%c \b", ch);
+
+ current_index--; /* overwrite last character */
+ }
+ } else if (ch == '\003') { /* exit on Ctrl+C */
+ printf("\n");
+ } else {
+ if (flags & RPP_SEVENBIT)
+ ch &= 0x7f;
+
+ if (isalpha((unsigned char)ch)) {
+ if(flags & RPP_FORCELOWER)
+ ch = tolower((unsigned char)ch);
+ if(flags & RPP_FORCEUPPER)
+ ch = toupper((unsigned char)ch);
+ }
+
+ outBuf[current_index++] = ch;
+ if(flags & RPP_ECHO_ON)
+ printf_s("%c", ch);
+ }
+ }
+
+ outBuf[current_index] = '\0';
+ _cputs("\n");
+
+ return outBuf;
+}
+
+#else /* !_WIN32 */
+
+#include <termios.h>
+#include <signal.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#ifndef TCSASOFT
+/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */
+# define TCSASOFT 0
+#endif
+
+/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
+#if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
+# define _POSIX_VDISABLE VDISABLE
+#endif
+
+static volatile sig_atomic_t signo[NSIG];
+
+static void handler(int);
+
+char *
+readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
+{
+ ssize_t nr;
+ int input, output, save_errno, i, need_restart;
+ char ch, *p, *end;
+ struct termios term, oterm;
+ struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
+ struct sigaction savetstp, savettin, savettou, savepipe;
+
+ /* I suppose we could alloc on demand in this case (XXX). */
+ if (bufsiz == 0) {
+ errno = EINVAL;
+ return(NULL);
+ }
+
+restart:
+ for (i = 0; i < NSIG; i++)
+ signo[i] = 0;
+ nr = -1;
+ save_errno = 0;
+ need_restart = 0;
+
+ /*
+ * Fix strange behaviour: a file must be open to prevent echo
+ */
+ if (!(flags & RPP_ECHO_ON))
+ open(_PATH_TTY, O_RDONLY);
+
+ /*
+ * Read and write to /dev/tty if available. If not, read from
+ * stdin and write to stderr unless a tty is required.
+ */
+ if ((flags & RPP_STDIN) ||
+ (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
+ if (flags & RPP_REQUIRE_TTY) {
+ errno = ENOTTY;
+ return(NULL);
+ }
+ input = STDIN_FILENO;
+ output = STDERR_FILENO;
+ }
+
+ /*
+ * Turn off echo if possible.
+ * If we are using a tty but are not the foreground pgrp this will
+ * generate SIGTTOU, so do it *before* installing the signal handlers.
+ */
+ if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
+ memcpy(&term, &oterm, sizeof(term));
+ if (!(flags & RPP_ECHO_ON))
+ term.c_lflag &= ~(ECHO | ECHONL);
+#ifdef VSTATUS
+ if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
+ term.c_cc[VSTATUS] = _POSIX_VDISABLE;
+#endif
+ (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
+ } else {
+ memset(&term, 0, sizeof(term));
+ term.c_lflag |= ECHO;
+ memset(&oterm, 0, sizeof(oterm));
+ oterm.c_lflag |= ECHO;
+ }
+
+ /*
+ * Catch signals that would otherwise cause the user to end
+ * up with echo turned off in the shell. Don't worry about
+ * things like SIGXCPU and SIGVTALRM for now.
+ */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* don't restart system calls */
+ sa.sa_handler = handler;
+ size_t writen = 0;
+ (void)sigaction(SIGALRM, &sa, &savealrm);
+ (void)sigaction(SIGHUP, &sa, &savehup);
+ (void)sigaction(SIGINT, &sa, &saveint);
+ (void)sigaction(SIGPIPE, &sa, &savepipe);
+ (void)sigaction(SIGQUIT, &sa, &savequit);
+ (void)sigaction(SIGTERM, &sa, &saveterm);
+ (void)sigaction(SIGTSTP, &sa, &savetstp);
+ (void)sigaction(SIGTTIN, &sa, &savettin);
+ (void)sigaction(SIGTTOU, &sa, &savettou);
+
+ if (!(flags & RPP_STDIN))
+ writen = write(output, prompt, strlen(prompt));
+ if(writen == 0) {
+ printf("unable to prompt.");
+ }
+ end = buf + bufsiz - 1;
+ p = buf;
+ while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
+ if (p < end) {
+ if ((flags & RPP_SEVENBIT))
+ ch &= 0x7f;
+ if (isalpha((unsigned char)ch)) {
+ if ((flags & RPP_FORCELOWER))
+ ch = (char)tolower((unsigned char)ch);
+ if ((flags & RPP_FORCEUPPER))
+ ch = (char)toupper((unsigned char)ch);
+ }
+ *p++ = ch;
+ }
+ }
+ *p = '\0';
+ save_errno = errno;
+ if (!(term.c_lflag & ECHO))
+ writen = write(output, "\n", 1);
+
+ if(writen == 0){
+ printf("unable to echo.");
+ }
+
+ /* Restore old terminal settings and signals. */
+ if (memcmp(&term, &oterm, sizeof(term)) != 0) {
+ const int sigttou = signo[SIGTTOU];
+
+ /* Ignore SIGTTOU generated when we are not the fg pgrp. */
+ while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
+ errno == EINTR && !signo[SIGTTOU])
+ continue;
+ signo[SIGTTOU] = sigttou;
+ }
+ (void)sigaction(SIGALRM, &savealrm, NULL);
+ (void)sigaction(SIGHUP, &savehup, NULL);
+ (void)sigaction(SIGINT, &saveint, NULL);
+ (void)sigaction(SIGQUIT, &savequit, NULL);
+ (void)sigaction(SIGPIPE, &savepipe, NULL);
+ (void)sigaction(SIGTERM, &saveterm, NULL);
+ (void)sigaction(SIGTSTP, &savetstp, NULL);
+ (void)sigaction(SIGTTIN, &savettin, NULL);
+ (void)sigaction(SIGTTOU, &savettou, NULL);
+ if (input != STDIN_FILENO)
+ (void)close(input);
+
+ /*
+ * If we were interrupted by a signal, resend it to ourselves
+ * now that we have restored the signal handlers.
+ */
+ for (i = 0; i < NSIG; i++) {
+ if (signo[i]) {
+ kill(getpid(), i);
+ switch (i) {
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ need_restart = 1;
+ }
+ }
+ }
+ if (need_restart)
+ goto restart;
+
+ if (save_errno)
+ errno = save_errno;
+ return(nr == -1 ? NULL : buf);
+}
+
+#if 0
+char *
+getpass(const char *prompt)
+{
+ static char buf[_PASSWORD_LEN + 1];
+
+ return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
+}
+#endif
+
+static void handler(int s)
+{
+
+ signo[s] = 1;
+}
+
+#endif /* !_WIN32 */
diff --git a/src/readpassphrase.h b/src/readpassphrase.h
new file mode 100644
index 0000000..5fc0bc2
--- /dev/null
+++ b/src/readpassphrase.h
@@ -0,0 +1,23 @@
+#ifndef READPASSPHRASE_H
+#define READPASSPHRASE_H
+
+#ifndef _PATH_TTY
+# define _PATH_TTY "/dev/tty"
+#endif
+
+#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
+#define RPP_ECHO_ON 0x01 /* Leave echo on. */
+#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
+#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
+#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
+#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
+#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */
+
+#include <stddef.h>
+
+char *readpassphrase(const char *prompt, char *outBuf, size_t outBufLen, int flags);
+
+#endif /* READPASSPHRASE_H */
+
+
+
diff --git a/src/sha3.c b/src/sha3.c
new file mode 100644
index 0000000..15a726f
--- /dev/null
+++ b/src/sha3.c
@@ -0,0 +1,296 @@
+/* -------------------------------------------------------------------------
+ * Works when compiled for either 32-bit or 64-bit targets, optimized for
+ * 64 bit.
+ *
+ * Canonical implementation of Init/Update/Finalize for SHA-3 byte input.
+ *
+ * SHA3-256, SHA3-384, SHA-512 are implemented. SHA-224 can easily be added.
+ *
+ * Based on code from http://keccak.noekeon.org/ .
+ *
+ * I place the code that I wrote into public domain, free to use.
+ *
+ * I would appreciate if you give credits to this work if you used it to
+ * write or test * your code.
+ *
+ * Aug 2015. Andrey Jivsov. crypto@brainhub.org
+ * ---------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "sha3.h"
+
+#define SHA3_ASSERT( x )
+#if defined(_MSC_VER)
+#define SHA3_TRACE( format, ...)
+#define SHA3_TRACE_BUF( format, buf, l, ...)
+#else
+#define SHA3_TRACE(format, args...)
+#define SHA3_TRACE_BUF(format, buf, l, args...)
+#endif
+
+//#define SHA3_USE_KECCAK
+/*
+ * Define SHA3_USE_KECCAK to run "pure" Keccak, as opposed to SHA3.
+ * The tests that this macro enables use the input and output from [Keccak]
+ * (see the reference below). The used test vectors aren't correct for SHA3,
+ * however, they are helpful to verify the implementation.
+ * SHA3_USE_KECCAK only changes one line of code in Finalize.
+ */
+
+#if defined(_MSC_VER)
+#define SHA3_CONST(x) x
+#else
+#define SHA3_CONST(x) x##L
+#endif
+
+#ifndef SHA3_ROTL64
+#define SHA3_ROTL64(x, y) \
+ (((x) << (y)) | ((x) >> ((sizeof(uint64_t)*8) - (y))))
+#endif
+
+static const uint64_t keccakf_rndc[24] = {
+ SHA3_CONST(0x0000000000000001UL), SHA3_CONST(0x0000000000008082UL),
+ SHA3_CONST(0x800000000000808aUL), SHA3_CONST(0x8000000080008000UL),
+ SHA3_CONST(0x000000000000808bUL), SHA3_CONST(0x0000000080000001UL),
+ SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008009UL),
+ SHA3_CONST(0x000000000000008aUL), SHA3_CONST(0x0000000000000088UL),
+ SHA3_CONST(0x0000000080008009UL), SHA3_CONST(0x000000008000000aUL),
+ SHA3_CONST(0x000000008000808bUL), SHA3_CONST(0x800000000000008bUL),
+ SHA3_CONST(0x8000000000008089UL), SHA3_CONST(0x8000000000008003UL),
+ SHA3_CONST(0x8000000000008002UL), SHA3_CONST(0x8000000000000080UL),
+ SHA3_CONST(0x000000000000800aUL), SHA3_CONST(0x800000008000000aUL),
+ SHA3_CONST(0x8000000080008081UL), SHA3_CONST(0x8000000000008080UL),
+ SHA3_CONST(0x0000000080000001UL), SHA3_CONST(0x8000000080008008UL)
+};
+
+static const unsigned keccakf_rotc[24] = {
+ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62,
+ 18, 39, 61, 20, 44
+};
+
+static const unsigned keccakf_piln[24] = {
+ 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20,
+ 14, 22, 9, 6, 1
+};
+
+/* generally called after SHA3_KECCAK_SPONGE_WORDS-ctx->capacityWords words
+ * are XORed into the state s
+ */
+static void
+keccakf(uint64_t s[25])
+{
+ int i, j, round;
+ uint64_t t, bc[5];
+#define KECCAK_ROUNDS 24
+
+ for(round = 0; round < KECCAK_ROUNDS; round++) {
+
+ /* Theta */
+ for(i = 0; i < 5; i++)
+ bc[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20];
+
+ for(i = 0; i < 5; i++) {
+ t = bc[(i + 4) % 5] ^ SHA3_ROTL64(bc[(i + 1) % 5], 1);
+ for(j = 0; j < 25; j += 5)
+ s[j + i] ^= t;
+ }
+
+ /* Rho Pi */
+ t = s[1];
+ for(i = 0; i < 24; i++) {
+ j = keccakf_piln[i];
+ bc[0] = s[j];
+ s[j] = SHA3_ROTL64(t, keccakf_rotc[i]);
+ t = bc[0];
+ }
+
+ /* Chi */
+ for(j = 0; j < 25; j += 5) {
+ for(i = 0; i < 5; i++)
+ bc[i] = s[j + i];
+ for(i = 0; i < 5; i++)
+ s[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
+ }
+
+ /* Iota */
+ s[0] ^= keccakf_rndc[round];
+ }
+}
+
+/* *************************** Public Inteface ************************ */
+
+/* For Init or Reset call these: */
+void
+sha3_Init256(void *priv)
+{
+ sha3_context *ctx = (sha3_context *) priv;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->capacityWords = 2 * 256 / (8 * sizeof(uint64_t));
+}
+
+void
+sha3_Init384(void *priv)
+{
+ sha3_context *ctx = (sha3_context *) priv;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->capacityWords = 2 * 384 / (8 * sizeof(uint64_t));
+}
+
+void
+sha3_Init512(void *priv)
+{
+ sha3_context *ctx = (sha3_context *) priv;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->capacityWords = 2 * 512 / (8 * sizeof(uint64_t));
+}
+
+void
+sha3_Update(void *priv, void const *bufIn, size_t len)
+{
+ sha3_context *ctx = (sha3_context *) priv;
+
+ /* 0...7 -- how much is needed to have a word */
+ unsigned old_tail = (8 - ctx->byteIndex) & 7;
+
+ size_t words;
+ unsigned tail;
+ size_t i;
+
+ const uint8_t *buf = bufIn;
+
+ SHA3_TRACE_BUF("called to update with:", buf, len);
+
+ SHA3_ASSERT(ctx->byteIndex < 8);
+ SHA3_ASSERT(ctx->wordIndex < sizeof(ctx->s) / sizeof(ctx->s[0]));
+
+ if(len < old_tail) { /* have no complete word or haven't started
+ * the word yet */
+ SHA3_TRACE("because %d<%d, store it and return", (unsigned)len,
+ (unsigned)old_tail);
+ /* endian-independent code follows: */
+ while (len--)
+ ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8);
+ SHA3_ASSERT(ctx->byteIndex < 8);
+ return;
+ }
+
+ if(old_tail) { /* will have one word to process */
+ SHA3_TRACE("completing one word with %d bytes", (unsigned)old_tail);
+ /* endian-independent code follows: */
+ len -= old_tail;
+ while (old_tail--)
+ ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8);
+
+ /* now ready to add saved to the sponge */
+ ctx->s[ctx->wordIndex] ^= ctx->saved;
+ SHA3_ASSERT(ctx->byteIndex == 8);
+ ctx->byteIndex = 0;
+ ctx->saved = 0;
+ if(++ctx->wordIndex ==
+ (SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords)) {
+ keccakf(ctx->s);
+ ctx->wordIndex = 0;
+ }
+ }
+
+ /* now work in full words directly from input */
+
+ SHA3_ASSERT(ctx->byteIndex == 0);
+
+ words = len / sizeof(uint64_t);
+ tail = len - words * sizeof(uint64_t);
+
+ SHA3_TRACE("have %d full words to process", (unsigned)words);
+
+ for(i = 0; i < words; i++, buf += sizeof(uint64_t)) {
+ const uint64_t t = (uint64_t) (buf[0]) |
+ ((uint64_t) (buf[1]) << 8 * 1) |
+ ((uint64_t) (buf[2]) << 8 * 2) |
+ ((uint64_t) (buf[3]) << 8 * 3) |
+ ((uint64_t) (buf[4]) << 8 * 4) |
+ ((uint64_t) (buf[5]) << 8 * 5) |
+ ((uint64_t) (buf[6]) << 8 * 6) |
+ ((uint64_t) (buf[7]) << 8 * 7);
+#if defined(__x86_64__ ) || defined(__i386__)
+ SHA3_ASSERT(memcmp(&t, buf, 8) == 0);
+#endif
+ ctx->s[ctx->wordIndex] ^= t;
+ if(++ctx->wordIndex ==
+ (SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords)) {
+ keccakf(ctx->s);
+ ctx->wordIndex = 0;
+ }
+ }
+
+ SHA3_TRACE("have %d bytes left to process, save them", (unsigned)tail);
+
+ /* finally, save the partial word */
+ SHA3_ASSERT(ctx->byteIndex == 0 && tail < 8);
+ while (tail--) {
+ SHA3_TRACE("Store byte %02x '%c'", *buf, *buf);
+ ctx->saved |= (uint64_t) (*(buf++)) << ((ctx->byteIndex++) * 8);
+ }
+ SHA3_ASSERT(ctx->byteIndex < 8);
+ SHA3_TRACE("Have saved=0x%016" PRIx64 " at the end", ctx->saved);
+}
+
+/* This is simply the 'update' with the padding block.
+ * The padding block is 0x01 || 0x00* || 0x80. First 0x01 and last 0x80
+ * bytes are always present, but they can be the same byte.
+ */
+void const *
+sha3_Finalize(void *priv)
+{
+ sha3_context *ctx = (sha3_context *) priv;
+
+ SHA3_TRACE("called with %d bytes in the buffer", ctx->byteIndex);
+
+ /* Append 2-bit suffix 01, per SHA-3 spec. Instead of 1 for padding we
+ * use 1<<2 below. The 0x02 below corresponds to the suffix 01.
+ * Overall, we feed 0, then 1, and finally 1 to start padding. Without
+ * M || 01, we would simply use 1 to start padding. */
+
+#ifndef SHA3_USE_KECCAK
+ /* SHA3 version */
+ ctx->s[ctx->wordIndex] ^=
+ (ctx->saved ^ ((uint64_t) ((uint64_t) (0x02 | (1 << 2)) <<
+ ((ctx->byteIndex) * 8))));
+#else
+ /* For testing the "pure" Keccak version */
+ ctx->s[ctx->wordIndex] ^=
+ (ctx->saved ^ ((uint64_t) ((uint64_t) 1 << (ctx->byteIndex *
+ 8))));
+#endif
+
+ ctx->s[SHA3_KECCAK_SPONGE_WORDS - ctx->capacityWords - 1] ^=
+ SHA3_CONST(0x8000000000000000UL);
+ keccakf(ctx->s);
+
+ /* Return first bytes of the ctx->s. This conversion is not needed for
+ * little-endian platforms e.g. wrap with #if !defined(__BYTE_ORDER__)
+ * || !defined(__ORDER_LITTLE_ENDIAN__) || __BYTE_ORDER__!=__ORDER_LITTLE_ENDIAN__
+ * ... the conversion below ...
+ * #endif */
+ {
+ unsigned i;
+ for(i = 0; i < SHA3_KECCAK_SPONGE_WORDS; i++) {
+ const unsigned t1 = (uint32_t) ctx->s[i];
+ const unsigned t2 = (uint32_t) ((ctx->s[i] >> 16) >> 16);
+ ctx->sb[i * 8 + 0] = (uint8_t) (t1);
+ ctx->sb[i * 8 + 1] = (uint8_t) (t1 >> 8);
+ ctx->sb[i * 8 + 2] = (uint8_t) (t1 >> 16);
+ ctx->sb[i * 8 + 3] = (uint8_t) (t1 >> 24);
+ ctx->sb[i * 8 + 4] = (uint8_t) (t2);
+ ctx->sb[i * 8 + 5] = (uint8_t) (t2 >> 8);
+ ctx->sb[i * 8 + 6] = (uint8_t) (t2 >> 16);
+ ctx->sb[i * 8 + 7] = (uint8_t) (t2 >> 24);
+ }
+ }
+
+ SHA3_TRACE_BUF("Hash: (first 32 bytes)", ctx->sb, 256 / 8);
+
+ return (ctx->sb);
+}
diff --git a/src/sha3.h b/src/sha3.h
new file mode 100644
index 0000000..adcf1e2
--- /dev/null
+++ b/src/sha3.h
@@ -0,0 +1,50 @@
+#ifndef SHA3_H
+#define SHA3_H
+
+/* -------------------------------------------------------------------------
+ * Works when compiled for either 32-bit or 64-bit targets, optimized for
+ * 64 bit.
+ *
+ * Canonical implementation of Init/Update/Finalize for SHA-3 byte input.
+ *
+ * SHA3-256, SHA3-384, SHA-512 are implemented. SHA-224 can easily be added.
+ *
+ * Based on code from http://keccak.noekeon.org/ .
+ *
+ * I place the code that I wrote into public domain, free to use.
+ *
+ * I would appreciate if you give credits to this work if you used it to
+ * write or test * your code.
+ *
+ * Aug 2015. Andrey Jivsov. crypto@brainhub.org
+ * ---------------------------------------------------------------------- */
+
+/* 'Words' here refers to uint64_t */
+#define SHA3_KECCAK_SPONGE_WORDS \
+ (((1600)/8/*bits to byte*/)/sizeof(uint64_t))
+typedef struct sha3_context_ {
+ uint64_t saved; /* the portion of the input message that we
+ * didn't consume yet */
+ union { /* Keccak's state */
+ uint64_t s[SHA3_KECCAK_SPONGE_WORDS];
+ uint8_t sb[SHA3_KECCAK_SPONGE_WORDS * 8];
+ };
+ unsigned byteIndex; /* 0..7--the next byte after the set one
+ * (starts from 0; 0--none are buffered) */
+ unsigned wordIndex; /* 0..24--the next word to integrate input
+ * (starts from 0) */
+ unsigned capacityWords; /* the double size of the hash output in
+ * words (e.g. 16 for Keccak 512) */
+} sha3_context;
+
+
+/* For Init or Reset call these: */
+void sha3_Init256(void *priv);
+void sha3_Init384(void *priv);
+void sha3_Init512(void *priv);
+
+void sha3_Update(void *priv, void const *bufIn, size_t len);
+
+void const *sha3_Finalize(void *priv);
+
+#endif
diff --git a/src/tests.c b/src/tests.c
new file mode 100644
index 0000000..021ee48
--- /dev/null
+++ b/src/tests.c
@@ -0,0 +1,813 @@
+/* Inexact source code package.
+ *
+ * Written in 2019 by <ben@hackade.org>.
+ *
+ * To the extent possible under law, the author have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#define __USE_MINGW_ANSI_STDIO 1
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tests.h"
+#include "base64.h"
+#include "binhex.h"
+#include "curve25519.h"
+#include "norx_inexact.h"
+#include "sha3.h"
+#include "argon2.h"
+#include "inexact.h"
+#include "chacha20_drng.h"
+
+int test_all() {
+ printf("**** Test base64 functions ****\n\n");
+
+ printf("** Encoding **\n\n");
+
+ unsigned char test1_in[] = "foob";
+ size_t test1_in_len = 4;
+ size_t test1_expected_len = 9;
+ unsigned char test1_expected[] = "Zm9vYg==\n";
+ size_t test1_out_len = 0;
+
+ unsigned char *test1_out =
+ base64_encode(test1_in, test1_in_len, &test1_out_len);
+
+ printf("TEST 1\n");
+ printf("IN[%zu]:%s\n", test1_in_len, test1_in);
+ printf("OUT[%zu]:%s\n", test1_out_len, test1_out);
+ printf("RESULT:");
+
+ int test1_len_result = (test1_out_len == test1_expected_len);
+ if (test1_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test1_out);
+ return 1;
+ }
+ int test1_result = memcmp(test1_out, test1_expected, test1_out_len);
+ if (test1_result != 0) {
+ printf("ERROR\n\n");
+ free(test1_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test1_out);
+
+ unsigned char test2_in[] = "foobar";
+ size_t test2_in_len = 6;
+ size_t test2_expected_len = 9;
+ unsigned char test2_expected[] = "Zm9vYmFy\n";
+ size_t test2_out_len = 0;
+
+ unsigned char *test2_out =
+ base64_encode(test2_in, test2_in_len, &test2_out_len);
+
+ printf("TEST 2\n");
+ printf("IN[%zu]:%s\n", test2_in_len, test2_in);
+ printf("OUT[%zu]:%s\n", test2_out_len, test2_out);
+ printf("RESULT:");
+
+ int test2_len_result = (test2_out_len == test2_expected_len);
+ if (test2_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test2_out);
+ return 1;
+ }
+ int test2_result = memcmp(test2_out, test2_expected, test2_out_len);
+ if (test2_result != 0) {
+ printf("ERROR\n\n");
+ free(test2_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test2_out);
+
+ printf("** Decoding **\n\n");
+
+ unsigned char test3_in[] = "Zm9vYmE=\n";
+ size_t test3_in_len = 9;
+ size_t test3_expected_len = 5;
+ unsigned char test3_expected[] = "fooba";
+ size_t test3_out_len = 0;
+
+ unsigned char *test3_out =
+ base64_decode(test3_in, test3_in_len, &test3_out_len);
+
+ printf("TEST 3\n");
+ printf("IN[%zu]:%s\n", test3_in_len, test3_in);
+ printf("OUT[%zu]:%s\n", test3_out_len, test3_out);
+ printf("RESULT:");
+
+ int test3_len_result = (test3_out_len == test3_expected_len);
+ if (test3_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test3_out);
+ return 1;
+ }
+ int test3_result = memcmp(test3_out, test3_expected, test3_out_len);
+ if (test3_result != 0) {
+ printf("ERROR\n\n");
+ free(test3_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test3_out);
+
+ unsigned char test4_in[] = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\nQUFBQUFBQUFBCg==\n";
+ size_t test4_in_len = 94;
+ size_t test4_expected_len = 67;
+ unsigned char test4_expected[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n";
+ size_t test4_out_len = 0;
+
+ unsigned char *test4_out =
+ base64_decode(test4_in, test4_in_len, &test4_out_len);
+
+ printf("TEST 4\n");
+ printf("IN[%zu]:%s\n", test4_in_len, test4_in);
+ printf("OUT[%zu]:%s\n", test4_out_len, test4_out);
+ printf("RESULT:");
+
+ int test4_len_result = (test4_out_len == test4_expected_len);
+ if (test4_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test4_out);
+ return 1;
+ }
+ int test4_result = memcmp(test4_out, test4_expected, test4_out_len);
+ if (test4_result != 0) {
+ printf("ERROR\n\n");
+ free(test4_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test4_out);
+
+ printf("**** Test base64 transformation functions ****\n\n");
+
+ printf("** Encoding **\n\n");
+
+ unsigned char test5_in[] = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\nYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\nYWFhYWFhYWFhYWFhYWFhYQ==\n";
+ size_t test5_in_len = 179;
+ size_t test5_expected_len = 174;
+ unsigned char test5_expected[] = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ";
+ size_t test5_out_len = 0;
+
+ unsigned char *test5_out =
+ b64t_encode(test5_in, test5_in_len, &test5_out_len);
+
+ printf("TEST 5\n");
+ printf("IN[%zu]:%s\n", test5_in_len, test5_in);
+ printf("OUT[%zu]:%s\n", test5_out_len, test5_out);
+ printf("RESULT:");
+
+ int test5_len_result = (test5_out_len == test5_expected_len);
+ if (test5_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test5_out);
+ return 1;
+ }
+ int test5_result = memcmp(test5_out, test5_expected, test5_out_len);
+ if (test5_result != 0) {
+ printf("ERROR\n\n");
+ free(test5_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test5_out);
+
+ unsigned char test6_in[] = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
+ size_t test6_in_len = 36;
+ size_t test6_expected_len = 36;
+ unsigned char test6_expected[] = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
+ size_t test6_out_len = 0;
+
+ unsigned char *test6_out =
+ b64t_encode(test6_in, test6_in_len, &test6_out_len);
+
+ printf("TEST 6\n");
+ printf("IN[%zu]:%s\n", test6_in_len, test6_in);
+ printf("OUT[%zu]:%s\n", test6_out_len, test6_out);
+ printf("RESULT:");
+
+ int test6_len_result = (test6_out_len == test6_expected_len);
+ if (test6_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test6_out);
+ return 1;
+ }
+ int test6_result = memcmp(test6_out, test6_expected, test6_out_len);
+ if (test6_result != 0) {
+ printf("ERROR\n\n");
+ free(test6_out);
+ return 1;
+ }
+ printf("OK\n\n");
+
+ printf("** Decoding **\n\n");
+
+ unsigned char test7_in[] = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ";
+ size_t test7_in_len = 174;
+ size_t test7_expected_len = 179;
+ unsigned char test7_expected[] = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\nYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\nYWFhYWFhYWFhYWFhYWFhYQ==\n";
+ size_t test7_out_len = 0;
+
+ unsigned char *test7_out =
+ b64t_decode(test7_in, test7_in_len, &test7_out_len);
+
+ printf("TEST 7\n");
+ printf("IN[%zu]:%s\n", test7_in_len, test7_in);
+ printf("OUT[%zu]:%s\n", test7_out_len, test7_out);
+ printf("RESULT:");
+
+ int test7_len_result = (test7_out_len == test7_expected_len);
+ if (test7_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test7_out);
+ return 1;
+ }
+ int test7_result = memcmp(test7_out, test7_expected, test7_out_len);
+ if (test7_result != 0) {
+ printf("ERROR\n\n");
+ free(test7_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test7_out);
+
+ unsigned char test8_in[] = "test1234";
+ size_t test8_in_len = 8;
+ size_t test8_expected_len = 9;
+ unsigned char test8_expected[] = "test1234\n";
+ size_t test8_out_len = 0;
+
+ unsigned char *test8_out =
+ b64t_decode(test8_in, test8_in_len, &test8_out_len);
+
+ printf("TEST 8\n");
+ printf("IN[%zu]:%s\n", test8_in_len, test8_in);
+ printf("OUT[%zu]:%s\n", test8_out_len, test8_out);
+ printf("RESULT:");
+
+ int test8_len_result = (test8_out_len == test8_expected_len);
+ if (test8_len_result == 0) {
+ printf("ERROR\n\n");
+ free(test8_out);
+ return 1;
+ }
+ int test8_result = memcmp(test8_out, test8_expected, test8_out_len);
+ if (test8_result != 0) {
+ printf("ERROR\n\n");
+ free(test8_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test8_out);
+
+
+ printf("TEST 8A\n");
+ unsigned char test8a[] = "0UkjGe6z0M7h5tfLqlgCQ5MhJjuINZO0IAUSr9QwqeUsXhrpptTjhm2sv06PFZH2nMmWJ8DgK5dJDuWP8ZoOIDg7XAY94pHyn376v2uMwKWKxH4789rsPwfa5cEFGAZU";
+ size_t test8a_len = 0;
+ unsigned char *test8a_base64 = base64_encode(test8a, 128, &test8a_len );
+ printf("IN[128]:%s\n", test8a);
+ printf("BASE64[%zu]:%s\n", test8a_len, test8a_base64);
+ size_t test8a_b64t_len = 0;
+ unsigned char *test8a_b64t = b64t_encode(test8a_base64, test8a_len, &test8a_b64t_len);
+ printf("B64T[%zu]:%s\n",test8a_b64t_len, test8a_b64t);
+ size_t test8a_b64t_decode_len = 0;
+ unsigned char *test8a_b64t_decode = b64t_decode(test8a_b64t, test8a_b64t_len, &test8a_b64t_decode_len);
+ printf("B64TD[%zu]:%s\n",test8a_b64t_decode_len,test8a_b64t_decode);
+ size_t test8a_origin_len = 0;
+ unsigned char *test8a_origin = base64_decode(test8a_b64t_decode, test8a_b64t_decode_len, &test8a_origin_len);
+ printf("DECODED[%zu]:%s\n", test8a_origin_len, test8a_origin);
+ printf("RESULT:");
+ if(test8a_origin_len != 128) {
+ printf("ERROR\n\n");
+ free(test8a_base64);
+ free(test8a_b64t);
+ free(test8a_b64t_decode);
+ free(test8a_origin);
+ return 1;
+ }
+ if(memcmp(test8a_origin,test8a,128) != 0) {
+ printf("ERROR\n\n");
+ free(test8a_base64);
+ free(test8a_b64t);
+ free(test8a_b64t_decode);
+ free(test8a_origin);
+ return 1;
+ }
+ free(test8a_base64);
+ free(test8a_b64t);
+ free(test8a_b64t_decode);
+ free(test8a_origin);
+ printf("OK\n\n");
+
+ printf("**** Test binary-hexadecimal conversion functions ****\n\n");
+
+ printf("** bin2hex **\n\n");
+
+ unsigned char test9_in[] = {0x66, 0x68, 0x77, 0x78, 0x00};
+ size_t test9_in_len = 4;
+ size_t test9_expected_len = 8;
+ unsigned char test9_expected[] = "66687778";
+
+ char *test9_out = bin2hex(test9_in, test9_in_len);
+
+ printf("TEST 9\n");
+ printf("IN[%zu]:%s\n", test9_in_len, test9_in);
+ printf("OUT:%s\n", test9_out);
+ printf("RESULT:");
+
+ int test9_result = memcmp(test9_out, test9_expected, test9_expected_len);
+ if (test9_result != 0) {
+ printf("ERROR\n\n");
+ free(test9_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test9_out);
+
+ printf("**** Test SHA3 functions ****\n\n");
+
+ printf("** SHA3-256 **\n\n");
+
+ unsigned char test10_in[] = "abc";
+ size_t test10_in_len = 3;
+ size_t test10_expected_len = 32;
+ unsigned char test10_expected[] =
+ "3A985DA74FE225B2045C172D6BD390BD855F086E3E9D525B46BFE24511431532";
+
+ sha3_context test10_ctx;
+ const uint8_t *test10_hash;
+ sha3_Init256(&test10_ctx);
+ sha3_Update(&test10_ctx, test10_in, test10_in_len);
+ test10_hash = sha3_Finalize(&test10_ctx);
+
+ char *test10_out = bin2hex(test10_hash, test10_expected_len);
+
+ printf("TEST 10\n");
+ printf("IN[%zu]:%s\n", test10_in_len, test10_in);
+ printf("OUT:%s\n", test10_out);
+ printf("RESULT:");
+
+ int test10_result =
+ memcmp(test10_out, test10_expected, test10_expected_len * 2);
+ if (test10_result != 0) {
+ printf("ERROR\n\n");
+ free(test10_out);
+ return 1;
+ }
+ printf("OK\n\n");
+ free(test10_out);
+
+ printf("**** Test curve 25519 functions ****\n\n");
+
+ printf("** Diffie-Hellman **\n\n");
+
+ curve25519_key S_a = {0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d,
+ 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45,
+ 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
+ 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a};
+ curve25519_key P_a = {0};
+ curve25519_key S_b = {0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b,
+ 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6,
+ 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd,
+ 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb};
+ curve25519_key P_b = {0};
+ curve25519_key shared_1 = {0};
+ curve25519_key shared_2 = {0};
+ curve25519_donna_basepoint(P_a, S_a);
+ curve25519_donna_basepoint(P_b, S_b);
+ curve25519_donna(shared_1, S_a, P_b);
+ curve25519_donna(shared_2, S_b, P_a);
+ char *S_a_str = bin2hex(S_a, sizeof(S_a));
+ char *P_a_str = bin2hex(P_a, sizeof(P_a));
+ char *S_b_str = bin2hex(S_b, sizeof(S_b));
+ char *P_b_str = bin2hex(P_b, sizeof(P_b));
+ char *shared_1_str = bin2hex(shared_1, sizeof(shared_1));
+ char *shared_2_str = bin2hex(shared_2, sizeof(shared_2));
+ printf("TEST 11\n");
+ printf("S_a=%s\n", S_a_str);
+ printf("P_a=%s\n", P_a_str);
+ printf("S_b=%s\n", S_b_str);
+ printf("P_b=%s\n", P_b_str);
+ printf("shared_1=%s\n", shared_1_str);
+ printf("shared_2=%s\n", shared_2_str);
+ free(S_a_str);
+ free(P_a_str);
+ free(S_b_str);
+ free(P_b_str);
+ free(shared_1_str);
+ free(shared_2_str);
+
+ curve25519_key expected_P_a = {
+ 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d,
+ 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38,
+ 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a};
+ curve25519_key expected_P_b = {
+ 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61,
+ 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78,
+ 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f};
+ curve25519_key expected_shared = {
+ 0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b,
+ 0xf4, 0x80, 0x35, 0x0f, 0x25, 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1,
+ 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42};
+
+ int test11 = memcmp(P_a, expected_P_a, sizeof(curve25519_key));
+ printf("RESULT:");
+ if (test11 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+ int test12 = memcmp(P_b, expected_P_b, sizeof(curve25519_key));
+ printf("TEST 12\n");
+ printf("RESULT:");
+ if (test12 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+ int test13 = memcmp(shared_1, expected_shared, sizeof(curve25519_key));
+ printf("TEST 13\n");
+ printf("RESULT:");
+ if (test13 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+ int test14 = memcmp(shared_2, expected_shared, sizeof(curve25519_key));
+ printf("TEST 14\n");
+ printf("RESULT:");
+ if (test14 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+
+ printf("**** Test norx functions ****\n\n");
+
+ printf("** test vectors encrypt **\n\n");
+
+ /* test vectors */
+ printf("TEST 15\n");
+ unsigned i;
+
+ unsigned char K[32];
+ for (i = 0; i < sizeof(K); ++i) K[i] = 255 & i;
+ char *K_str = bin2hex(K, sizeof(K));
+ printf("K: %s\n", K_str);
+ free(K_str);
+
+ unsigned char N[32];
+ for (i = 0; i < sizeof(N); ++i) N[i] = 255 & (i + 0x20);
+ char *N_str = bin2hex(N, sizeof(N));
+ printf("N: %s\n", N_str);
+ free(N_str);
+
+ unsigned char A[128];
+ for (i = 0; i < sizeof(A); ++i) A[i] = 255 & i;
+ char *A_str = bin2hex(A, sizeof(A));
+ printf("A: %s\n", A_str);
+ free(A_str);
+
+ unsigned char M[128];
+ for (i = 0; i < sizeof(M); ++i) M[i] = 255 & i;
+ char *M_str = bin2hex(M, sizeof(M));
+ printf("M: %s\n", M_str);
+ free(M_str);
+
+ unsigned char Z[128];
+ for (i = 0; i < sizeof(Z); ++i) Z[i] = 255 & i;
+ char *Z_str = bin2hex(Z, sizeof(Z));
+ printf("Z: %s\n", Z_str);
+ free(Z_str);
+
+ unsigned char C[160] = {0};
+ size_t C_len = 0;
+
+ norx_aead_encrypt(C, &C_len, A, sizeof(A), M, sizeof(M), Z, sizeof(Z), N, K,
+ 256);
+
+ char *C_str = bin2hex(C, C_len);
+ printf("IN[%zu]:%s\n", C_len, C_str);
+ free(C_str);
+
+ unsigned char nox_test_vectors[] = {
+ 0x50, 0xCE, 0x69, 0x2C, 0x19, 0xCB, 0x91, 0x02, 0xC6, 0x12, 0x96, 0x6F,
+ 0x0F, 0x62, 0x6B, 0x62, 0x96, 0xDE, 0x89, 0x27, 0x1C, 0x98, 0x29, 0x10,
+ 0xAA, 0xC1, 0xC3, 0x55, 0x52, 0x2E, 0x8F, 0xA7, 0x13, 0x03, 0xF8, 0xD5,
+ 0xC9, 0xDE, 0x39, 0x04, 0x84, 0xBA, 0x91, 0xA9, 0x94, 0xCF, 0xF9, 0x1B,
+ 0xF7, 0x15, 0xD6, 0xCB, 0x22, 0xCC, 0x00, 0xF3, 0x64, 0x02, 0x10, 0x03,
+ 0x17, 0x19, 0x61, 0x68, 0x72, 0x39, 0xDD, 0x94, 0x53, 0x02, 0x9B, 0x87,
+ 0x85, 0x9C, 0x10, 0x93, 0x21, 0x13, 0x59, 0x40, 0xBC, 0x1B, 0xC8, 0x1A,
+ 0x55, 0xA9, 0x51, 0xC7, 0x1B, 0x29, 0x42, 0xFF, 0xDE, 0xBF, 0x8D, 0x13,
+ 0xC4, 0xF3, 0x87, 0x2B, 0x78, 0xD4, 0x50, 0x6F, 0x40, 0xDB, 0x65, 0x3C,
+ 0xE3, 0xB8, 0xD2, 0xBE, 0xA7, 0xA2, 0xF9, 0xE9, 0x7F, 0xF4, 0x56, 0xB7,
+ 0xF0, 0xDB, 0x8C, 0x92, 0x27, 0xE2, 0x2F, 0x23, 0xA0, 0xD1, 0x0D, 0x28,
+ 0x52, 0x91, 0xBE, 0xDB, 0x7B, 0x7C, 0xBD, 0xC4, 0x7E, 0x0F, 0xE2, 0x38,
+ 0x5B, 0xF5, 0x5B, 0xC5, 0xF0, 0x57, 0xBC, 0xAB, 0x2C, 0x57, 0xCC, 0xD0,
+ 0x83, 0xD2, 0x9B, 0x2C};
+
+ int test15 = memcmp(C, nox_test_vectors, sizeof(nox_test_vectors));
+ printf("RESULT:");
+ if (test15 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+
+ printf("** encrypt/decrypt 256 bits **\n\n");
+
+ unsigned char crypted[1024] = {0};
+ unsigned char header[1024] = {'Z'};
+ size_t crypted_len = 0;
+ unsigned char message[] = "Alice and Bob message's are secret";
+ unsigned char nonce[32] = {0};
+ struct chacha20_drng *drng;
+ int ret = drng_chacha20_init(&drng);
+ if (ret) {
+ printf("Allocation failed: %d\n", ret);
+ return 1;
+ }
+ if (drng_chacha20_get(drng, nonce, sizeof(nonce))) {
+ printf("Getting random numbers failed\n");
+ return 1;
+ }
+ unsigned char key[] = "AA0123456789ABCDEF0123456789AZERTY";
+
+ norx_aead_encrypt(crypted, &crypted_len, header, 8, message,
+ sizeof(message) - 1, NULL, 0, nonce, key, 256);
+
+ printf("TEST 16\n");
+ printf("IN[%zu]:%s\n", sizeof(message) - 1, message);
+ char *nonce_str = bin2hex(nonce, sizeof(nonce));
+ printf("NONCE[%zu]:%s\n", sizeof(nonce), nonce_str);
+ free(nonce_str);
+ printf("KEY[%zu]:%s\n", sizeof(key) - 1, key);
+
+ char *crypted_str = bin2hex(crypted, crypted_len);
+ printf("OUT[%zu]: %s\n", crypted_len, crypted_str);
+ free(crypted_str);
+
+ unsigned char clear[1024] = {0};
+ size_t clear_len = 0;
+
+ int test16 = norx_aead_decrypt(clear, &clear_len, header, 8, crypted,
+ crypted_len, NULL, 0, nonce, key, 256);
+
+ printf("RESULT:");
+ if (test16 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+ drng_chacha20_destroy(drng);
+
+ printf("TEST 17\n");
+ char *clear_str = bin2hex(clear, clear_len);
+ printf("CLEAR[%zu]:%s\n", clear_len, clear);
+ free(clear_str);
+
+ int test17 = memcmp(clear, message, sizeof(message));
+ printf("RESULT:");
+ if (test17 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+
+ printf("TEST 18\n");
+ header[2] = 1; // corrupted buffer test
+ int test18 = norx_aead_decrypt(clear, &clear_len, header, 8, crypted,
+ crypted_len, NULL, 0, nonce, key, 256);
+
+ printf("RESULT:");
+ if (test18 != 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+
+ printf("** encrypt/decrypt 256 bits with 32 bits tag **\n\n");
+
+ unsigned char cryptedt0[1024] = {0};
+ size_t crypted_lent0 = 0;
+ unsigned char messaget0[] = "Simple message encryption";
+ unsigned char noncet0[32] = {0};
+ struct chacha20_drng *drng0;
+ int ret0 = drng_chacha20_init(&drng0);
+ if (ret0) {
+ printf("Allocation failed: %d\n", ret);
+ return 1;
+ }
+ if (drng_chacha20_get(drng, noncet0, sizeof(noncet0))) {
+ printf("Getting random numbers failed\n");
+ return 1;
+ }
+ unsigned char keyt0[] = "AA0123456789ABCDEF0123456789AZERTY";
+
+ norx_aead_encrypt(cryptedt0, &crypted_lent0, NULL, 0, messaget0,
+ sizeof(messaget0) - 1, NULL, 0, noncet0, keyt0, 32);
+
+ printf("TEST 19\n");
+ printf("IN[%zu]:%s\n", sizeof(messaget0) - 1, messaget0);
+ char *nonce_strt0 = bin2hex(noncet0, sizeof(noncet0));
+ printf("NONCE[%zu]:%s\n", sizeof(noncet0), nonce_strt0);
+ free(nonce_strt0);
+ printf("KEY[%zu]:%s\n", sizeof(key) - 1, key);
+
+ char *crypted_strt0 = bin2hex(cryptedt0, crypted_lent0);
+ printf("OUT[%zu]:%s\n", crypted_lent0, crypted_strt0);
+ free(crypted_strt0);
+
+ unsigned char cleart0[1024] = {0};
+ size_t clear_lent0 = 0;
+
+ int test19 = norx_aead_decrypt(cleart0, &clear_lent0, NULL, 0, cryptedt0,
+ crypted_lent0, NULL, 0, noncet0, keyt0, 32);
+
+ printf("RESULT:");
+ if (test19 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+
+ printf("TEST 20\n");
+ char *clear_strt0 = bin2hex(cleart0, clear_lent0);
+ printf("CLEAR[%zu]:%s\n", clear_lent0, cleart0);
+ free(clear_strt0);
+
+ int test20 = memcmp(cleart0, messaget0, sizeof(messaget0));
+ printf("RESULT:");
+ if (test20 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+ drng_chacha20_destroy(drng0);
+
+ printf("**** Test argon2 functions ****\n\n");
+
+ printf("** Argon2i -t 2 -m 16 -p 4 -l 24 **\n\n");
+
+ unsigned char test21_password[] = "password";
+ size_t test21_password_len = 8;
+ unsigned char test21_salt[] = "somesalt";
+ size_t test21_salt_len = 8;
+ unsigned char test21_out[24];
+ size_t test21_out_len = 24;
+ printf("TEST 21\n");
+ printf("PASS[8]:%s\n", test21_password);
+ printf("SALT[4]:%s\n", test21_salt);
+
+ int test21 = argon2i_hash_raw( 2,
+ 1<<16,
+ 4,
+ test21_password,
+ test21_password_len,
+ test21_salt,
+ test21_salt_len,
+ test21_out,
+ test21_out_len);
+
+ char *test21_out_str = bin2hex(test21_out, test21_out_len);
+ printf("OUT[24]:%s\n", test21_out_str);
+ printf("RESULT:");
+ if (test21 != 0) {
+ printf("ERROR\n\n");
+ free(test21_out_str);
+ return 1;
+ }
+ if(memcmp(test21_out_str, "45D7AC72E76F242B20B77B9BF9BF9D5915894E669A24E6C6", 48) == 0){
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ free(test21_out_str);
+ return 1;
+ }
+ free(test21_out_str);
+
+ printf("**** Test random number generator functions ****\n\n");
+
+ printf("** ChaCha20 DRNG **\n\n");
+
+ struct chacha20_drng *drng2;
+ uint8_t buf[40];
+ char version[30];
+ int ret2;
+
+ drng_chacha20_versionstring(version, sizeof(version));
+ printf("Obtained version string: %s\n", version);
+ printf("Obtained version number: %u\n", drng_chacha20_version());
+
+ ret2 = drng_chacha20_init(&drng2);
+ if (ret2) {
+ printf("Allocation failed: %d\n", ret2);
+ return 1;
+ }
+
+ if (drng_chacha20_get(drng2, buf, 32)) {
+ printf("Getting random numbers failed\n");
+ return 1;
+ }
+
+ char *test22 = bin2hex(buf,40);
+ printf("Random number:%s\n", test22);
+ free(test22);
+
+ if (drng_chacha20_get(drng2, buf, 32)) {
+ printf("Getting random numbers failed\n");
+ return 1;
+ }
+
+ test22 = bin2hex(buf,40);
+ printf("Random number:%s\n\n", test22);
+ free(test22);
+ drng_chacha20_destroy(drng2);
+
+
+
+ printf("**** Test inexact functions ****\n\n");
+
+ printf("** encrypt/decrypt n=32 t=32 **\n\n");
+
+ unsigned char testinexact1_in[] = "simple test message";
+ size_t testinexact1_in_len = 19;
+ curve25519_key testinexact1_seckey;
+ curve25519_key testinexact1_pubkey;
+ struct chacha20_drng *drng1;
+ int ret1 = drng_chacha20_init(&drng1);
+ if (ret1) {
+ printf("Allocation failed: %d\n", ret);
+ return 1;
+ }
+ if (drng_chacha20_get(drng, testinexact1_seckey, sizeof(curve25519_key))) {
+ printf("Getting random numbers failed\n");
+ return 1;
+ }
+ drng_chacha20_destroy(drng1);
+ curve25519_donna_basepoint(testinexact1_pubkey, testinexact1_seckey);
+ size_t testinexact1_out_len = 0;
+
+ printf("TEST INEXACT\n");
+ printf("IN[%zu]=%s\n", testinexact1_in_len, testinexact1_in);
+ char *testinexact1_seckey_str = bin2hex(testinexact1_seckey,32);
+ char *testinexact1_pubkey_str = bin2hex(testinexact1_pubkey,32);
+ printf("SECKEY[%zu]:%s\n", sizeof(curve25519_key), testinexact1_seckey_str);
+ printf("PUBKEY[%zu]:%s\n", sizeof(curve25519_key), testinexact1_pubkey_str);
+ free(testinexact1_seckey_str);
+ free(testinexact1_pubkey_str);
+
+ unsigned char *testinexact1_out = encrypt_data(testinexact1_seckey,
+ testinexact1_pubkey,
+ NULL,
+ testinexact1_in,
+ testinexact1_in_len,
+ 32,
+ 32,
+ 0,
+ &testinexact1_out_len);
+
+ printf("OUT[%zu]=%s\n", testinexact1_out_len, testinexact1_out);
+
+ size_t testinexact1_clear_len = 0;
+ unsigned char *testinexact1_clear = decrypt_data(testinexact1_seckey,
+ testinexact1_pubkey,
+ testinexact1_out,
+ testinexact1_out_len,
+ 0,
+ &testinexact1_clear_len);
+
+ testinexact1_clear[testinexact1_clear_len] = 0;
+ printf("CLEAR[%zu]=%s\n", testinexact1_clear_len, testinexact1_clear);
+
+ int testinexact1 = memcmp(testinexact1_clear, testinexact1_in, testinexact1_in_len);
+ printf("RESULT:");
+ if (testinexact1 == 0) {
+ printf("OK\n\n");
+ } else {
+ printf("ERROR\n\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/tests.h b/src/tests.h
new file mode 100644
index 0000000..9326c5a
--- /dev/null
+++ b/src/tests.h
@@ -0,0 +1,19 @@
+/* Inexact source code package.
+ *
+ * Written in 2019 by <ben@hackade.org>.
+ *
+ * To the extent possible under law, the author have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef TESTS_H
+#define TESTS_H
+
+int test_all();
+
+#endif /* TESTS_H */
+
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..ea1d3a2
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+function print_rand_str() {
+ set="abcdefghijklmonpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ n=32
+ rand=""
+ for i in `seq 1 $n`; do
+ char=${set:$RANDOM % ${#set}:1}
+ rand+=$char
+ done
+ echo $rand
+}
+
+rm -f *.key
+./inexact --no-password -g -k alices.key -p alicep.key
+./inexact --no-password -g -k bobs.key -p bobp.key
+MSG=$(print_rand_str)
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi
+MSG=$(print_rand_str)
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key --base64 | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi
+MSG=$(print_rand_str)
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key -c 300 | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi
+MSG=$(print_rand_str)
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key -n 300 | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi
+MSG=$(print_rand_str)
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key -t 64 | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi
+MSG=$(print_rand_str)
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key -t 64 -n 16 | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi
+MSG2=$(echo "$MSG" | ./inexact -e -k alices.key -p bobp.key -w | ./inexact -d -k bobs.key -p alicep.key )
+if [[ "$MSG" == "$MSG2" ]]
+then
+ echo "TEST: OK"
+else
+ echo "TEST: KO"
+fi