First public release

This commit is contained in:
ben
2019-05-27 21:40:41 +02:00
commit accdbc7806
50 changed files with 15267 additions and 0 deletions

64
Makefile Normal file
View File

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

138
README.md Normal file
View File

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

20
src/argon2-arch.c Normal file
View File

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

615
src/argon2-core.c Normal file
View File

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

226
src/argon2-core.h Normal file
View File

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

432
src/argon2-encoding.c Normal file
View File

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

40
src/argon2-encoding.h Normal file
View File

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

93
src/argon2-impl-select.c Normal file
View File

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

23
src/argon2-impl-select.h Normal file
View File

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

193
src/argon2-template-64.h Normal file
View File

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

36
src/argon2-thread.c Normal file
View File

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

47
src/argon2-thread.h Normal file
View File

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

476
src/argon2.c Normal file
View File

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

465
src/argon2.h Normal file
View File

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

5018
src/argtable3.c Normal file

File diff suppressed because it is too large Load Diff

305
src/argtable3.h Normal file
View File

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

253
src/base64.c Normal file
View File

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

21
src/base64.h Normal file
View File

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

67
src/binhex.c Normal file
View File

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

9
src/binhex.h Normal file
View File

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

90
src/blake2-impl.h Normal file
View File

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

225
src/blake2.c Normal file
View File

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

30
src/blake2.h Normal file
View File

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

675
src/chacha20_drng.c Normal file
View File

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

154
src/chacha20_drng.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

32
src/curve25519-donna.h Normal file
View File

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

27
src/curve25519.c Normal file
View File

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

10
src/curve25519.h Normal file
View File

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

1033
src/inexact.c Normal file

File diff suppressed because it is too large Load Diff

40
src/inexact.h Normal file
View File

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

555
src/main.c Normal file
View File

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

701
src/norx_inexact.c Normal file
View File

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

64
src/norx_inexact.h Normal file
View File

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

20
src/norx_inexact_config.h Normal file
View File

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

119
src/norx_inexact_util.h Normal file
View File

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

262
src/randombytes.c Normal file
View File

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

19
src/randombytes.h Normal file
View File

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

283
src/readpassphrase.c Normal file
View File

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

23
src/readpassphrase.h Normal file
View File

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

296
src/sha3.c Normal file
View File

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

50
src/sha3.h Normal file
View File

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

813
src/tests.c Normal file
View File

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

19
src/tests.h Normal file
View File

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

71
test.sh Executable file
View File

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