diff options
author | ben | 2019-05-27 21:40:41 +0200 |
---|---|---|
committer | ben | 2019-05-27 21:40:41 +0200 |
commit | accdbc780617a4f4b30790ffb25ab1d26652c315 (patch) | |
tree | 2633c1246a35e7242bfc693959830ae63b641301 | |
download | inexact-accdbc780617a4f4b30790ffb25ab1d26652c315.tar.gz inexact-accdbc780617a4f4b30790ffb25ab1d26652c315.tar.bz2 inexact-accdbc780617a4f4b30790ffb25ab1d26652c315.tar.xz |
First public release
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 */ + @@ -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 |