From a6d132af39c659fb1307a7e3c4a02987a3dba8ef Mon Sep 17 00:00:00 2001 From: Alexander Nutz Date: Sun, 7 Sep 2025 21:33:04 +0200 Subject: [PATCH] get rid of the unportable bs bigint.h bs; slowcrypt cli wip --- bigint.h | 2232 ----------------------------------------- poly1305.h | 179 ++++ slowcrypt/slowcrypt.c | 198 ++++ 3 files changed, 377 insertions(+), 2232 deletions(-) delete mode 100644 bigint.h create mode 100644 poly1305.h create mode 100644 slowcrypt/slowcrypt.c diff --git a/bigint.h b/bigint.h deleted file mode 100644 index ba90ac2..0000000 --- a/bigint.h +++ /dev/null @@ -1,2232 +0,0 @@ -/* SPDX-License-Identifier: 0BSD */ -/* Copyright (c) 2023 John Regan */ -/* Full license details available at end of file. */ -/* Original source: https://github.com/jprjr/bigint.git - * Commit: 26f0d7b27 - * With modifications - * - * See git history of this repo - */ -/* - * "Special" use case: using this in another single header library: - * #define BIGINT_FUNC MYTHING_FUNC - * #define BIGINT_NAMESPACE(x) mything_bigint_##x - * #define BIGINT_UNDEF - * #define BIGINT_NO_MALLOC - * #include "bigint.h" - */ - -#ifndef BIGINT_H -#define BIGINT_H - -#ifndef BIGINT_FUNC -#define BIGINT_FUNC /**/ -#endif - -#ifndef BIGINT_NAMESPACE -#define BIGINT_NAMESPACE(x) bigint_##x -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include -#include -#include - -#define BIGINT_ENOMEM 1 -#define BIGINT_ELIMIT 2 -#define BIGINT_EINVAL 4 - -/* if you override any of: - * BIGINT_WORD_WIDTH - * BIGINT_WORD_TYPE - * BIGINT_DEFAULT_LIMIT - * BIGINT_NO_MALLOC - * - * They needs to be defined before *every* #include - * (not just the implementation include) since that - * affects function signatures, the BIGINT_INIT - * initializer, and (for NO_MALLOC) the struct itself. */ - -#ifndef BIGINT_DEFAULT_LIMIT -#define BIGINT_DEFAULT_LIMIT 4096 -#endif - -#ifndef BIGINT_WORD_WIDTH -#define BIGINT_WORD_WIDTH 4 -#endif - -#ifndef BIGINT_WORD_TYPE -#if BIGINT_WORD_WIDTH == 1 -#define BIGINT_WORD_TYPE uint8_t -#elif BIGINT_WORD_WIDTH == 2 -#define BIGINT_WORD_TYPE uint16_t -#elif BIGINT_WORD_WIDTH == 4 -#define BIGINT_WORD_TYPE uint32_t -#elif BIGINT_WORD_WIDTH == 8 -#define BIGINT_WORD_TYPE uint64_t -#else -error unknown_type; -#endif -#endif - -typedef BIGINT_WORD_TYPE bigint_word; - -typedef struct bigint -{ - size_t size; - size_t alloc; - size_t limit; - size_t sign; -#ifdef BIGINT_NO_MALLOC - bigint_word words[(BIGINT_DEFAULT_LIMIT / BIGINT_WORD_WIDTH)]; - /* TODO: - * compiling with BIGINT_NO_MALLOC defined and the word type set to uint8_t - * generates the following warning: - * bigint.h:409:24: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] - * 409 | b->words[old_size] = val; - * | ~~~~~~~~~~~~~~~~~~~^~~~~ - * bigint.h: In function ‘bigint_from_string_base10’: - * bigint.h:82:17: note: at offset -1 into destination object ‘words’ of size 4096 - * 82 | bigint_word words[(BIGINT_DEFAULT_LIMIT/BIGINT_WORD_WIDTH)]; - * .... why? */ -#else - bigint_word* words; -#endif -} bigint; - -#define BIGINT_WORD_SIZE sizeof(bigint_word) -#define BIGINT_WORD_BIT (BIGINT_WORD_SIZE * CHAR_BIT) -#define BIGINT_WORD_MASK ((bigint_word) - 1) - -#define BIGINT_HALF_WORD_BIT (BIGINT_WORD_BIT / 2) -#define BIGINT_HALF_WORD_MASK (BIGINT_WORD_MASK >> BIGINT_HALF_WORD_BIT) - -#ifdef BIGINT_NO_MALLOC -#define BIGINT_INIT \ - {.size = 0, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT} -#else -#define BIGINT_INIT \ - {.size = 0, \ - .alloc = 0, \ - .sign = 0, \ - .limit = BIGINT_DEFAULT_LIMIT, \ - .words = NULL} -#endif -extern const bigint* BIGINT_ZERO; - -BIGINT_FUNC -void BIGINT_NAMESPACE(init)(bigint* b); - -BIGINT_FUNC -void BIGINT_NAMESPACE(free)(bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(copy)(bigint* b, const bigint* a); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_word)(bigint* b, bigint_word val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_string)(bigint* b, - const char* str, - size_t len, - unsigned int base); - -/* convenience function for NULL-terminated strings */ -BIGINT_FUNC -int BIGINT_NAMESPACE(from_cstring)(bigint* b, - const char* str, - unsigned int base); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u8)(bigint* b, uint8_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i8)(bigint* b, int8_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u16)(bigint* b, uint16_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i16)(bigint* b, int16_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u32)(bigint* b, uint32_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i32)(bigint* b, int32_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u64)(bigint* b, uint64_t val); - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i64)(bigint* b, int64_t val); - -/* bigint_to functions return 0 on success, 1 if the - * bigint won't fit into the type */ - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u8)(uint8_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i8)(int8_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u16)(uint16_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i16)(int16_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u32)(uint32_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i32)(int32_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u64)(uint64_t* val, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i64)(int64_t* val, const bigint* b); - -BIGINT_FUNC -size_t BIGINT_NAMESPACE(to_string)(char* str, - size_t len, - const bigint* b, - unsigned int base); - -BIGINT_FUNC -int BIGINT_NAMESPACE(cmp)(const bigint* a, const bigint* b); - -#define bigint_lt (a, b)(bigint_cmp((a), (b)) == -1) -#define bigint_gt (a, b)(bigint_cmp((a), (b)) == 1) -#define bigint_eq (a, b)(bigint_cmp((a), (b)) == 0) -#define bigint_le (a, b)(bigint_cmp((a), (b)) != 1) -#define bigint_ge (a, b)(bigint_cmp((a), (b)) != -1) - -BIGINT_FUNC -int BIGINT_NAMESPACE(inc)(bigint* c, const bigint* a); - -BIGINT_FUNC -int BIGINT_NAMESPACE(dec)(bigint* c, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(add)(bigint* c, const bigint* a, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(sub)(bigint* c, const bigint* a, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(mul)(bigint* c, const bigint* a, const bigint* b); - -BIGINT_FUNC -int BIGINT_NAMESPACE(div_mod)(bigint* quotient, - bigint* remainder, - const bigint* numerator, - const bigint* denominator); - -BIGINT_FUNC -int BIGINT_NAMESPACE(lshift)(bigint* c, const bigint* a, size_t bits); - -BIGINT_FUNC -int BIGINT_NAMESPACE(rshift)(bigint* c, const bigint* a, size_t bits); - -BIGINT_FUNC -int BIGINT_NAMESPACE(lshift_overwrite)(bigint* a, size_t bits); - -BIGINT_FUNC -int BIGINT_NAMESPACE(rshift_overwrite)(bigint* a, size_t bits); - -/* faster div_mod for when you're dividing by a regular, positive word type - note that this - * overwrites numerator! roughly equivalent to: - * remainder = numerator % denominator - * numerator /= denominator */ -BIGINT_FUNC -void BIGINT_NAMESPACE(div_mod_word)(bigint* numerator, - bigint_word* remainder, - bigint_word denominator); - -#ifdef __cplusplus -} -#endif - -#endif - -#ifdef BIGINT_IMPLEMENTATION - -#define BIGINT_BLOCK_SIZE 8 - -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) -#define BIGINT_UNREACHABLE __builtin_unreachable() -#define BIGINT_UNREACHABLE_RETURN(val) __builtin_unreachable() -#endif - -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) -#define BIGINT_NONNULL1 __attribute__((__nonnull__(1))) -#endif - -#if _MSC_VER >= 1310 -#define BIGINT_UNREACHABLE __assume(0) -#define BIGINT_UNREACHABLE_RETURN(val) __builtin_unreachable() -#endif - -#ifndef BIGINT_UNREACHABLE -#define BIGINT_UNREACHABLE assert(0) -#endif - -#ifndef BIGINT_NONNULL1 -#define BIGINT_NONNULL1 -#endif - -#ifndef BIGINT_UNREACHABLE_RETURN -#define BIGINT_UNREACHABLE_RETURN(val) return val -#endif - -#ifndef BIGINT_SINGLE_WORD_ONLY -#ifndef BIGINT_DWORD_TYPE -#if BIGINT_WORD_WIDTH == 8 -#if defined(__SIZEOF_INT128__) -#define BIGINT_DWORD_TYPE unsigned __int128 -#endif -#elif BIGINT_WORD_WIDTH == 4 -#define BIGINT_DWORD_TYPE uint64_t -#elif BIGINT_WORD_WIDTH == 2 -#define BIGINT_DWORD_TYPE uint32_t -#elif BIGINT_WORD_WIDTH == 1 -#define BIGINT_DWORD_TYPE uint16_t -#endif -#endif /* BIGINT_DWORD_TYPE */ -#endif /* !SINGLE_WORD_ONLY */ - -#ifdef _WIN32 -#include -#if _MSC_VER -#if BIGINT_WORD_WIDTH == 8 && defined(_WIN64) -#pragma intrinsic(_BitScanReverse64) -#pragma intrinsic(_umul128) -#elif BIGINT_WORD_WIDTH <= 4 -#pragma intrinsic(_BitScanReverse) -#endif -#endif -#endif - -#ifdef BIGINT_NO_MALLOC -static const bigint BIGINT_ZERO_STORAGE = {.size = 0, - .alloc = 0, - .sign = 0, - .limit = BIGINT_DEFAULT_LIMIT}; -#else -static const bigint BIGINT_ZERO_STORAGE = {.words = NULL, - .size = 0, - .alloc = 0, - .sign = 0, - .limit = BIGINT_DEFAULT_LIMIT}; -#endif -const bigint* BIGINT_ZERO = &BIGINT_ZERO_STORAGE; - -static void BIGINT_NAMESPACE(reset)(bigint* b); -static int BIGINT_NAMESPACE(resize)(bigint* b, size_t size); -static inline void BIGINT_NAMESPACE(truncate)(bigint* b); -static int BIGINT_NAMESPACE(append)(bigint* b, bigint_word val); - -static int BIGINT_NAMESPACE(add_word)(bigint* b, bigint_word val); - -static inline size_t BIGINT_NAMESPACE(word_bitlength)(bigint_word a); -static int BIGINT_NAMESPACE(word_add)(bigint_word* a, bigint_word b); -static int BIGINT_NAMESPACE(word_sub)(bigint_word* a, bigint_word b); -static bigint_word BIGINT_NAMESPACE(word_mul)(bigint_word* a, bigint_word b); - -static int BIGINT_NAMESPACE(add_unsigned)(bigint* a, const bigint* b); -static int BIGINT_NAMESPACE(sub_unsigned)(bigint* a, const bigint* b); - -static int BIGINT_NAMESPACE(add_signed)(bigint* c, - const bigint* a, - size_t a_sign, - const bigint* b, - size_t b_sign); - -static int BIGINT_NAMESPACE(cmp_abs)(const bigint* a, const bigint* b); - -static int BIGINT_NAMESPACE(mul_long)(bigint* c, - const bigint* a, - const bigint* b); - -static size_t BIGINT_NAMESPACE(bitlength)(const bigint* a); -static int BIGINT_NAMESPACE(get_bit)(const bigint* a, size_t b); -static int BIGINT_NAMESPACE(set_bit)(bigint* a, size_t b, uint8_t val); -static int BIGINT_NAMESPACE(from_string_base16)(bigint* b, - const char* str, - size_t len); -static int BIGINT_NAMESPACE(from_string_base10)(bigint* b, - const char* str, - size_t len); -static int BIGINT_NAMESPACE(from_string_base8)(bigint* b, - const char* str, - size_t len); -static int BIGINT_NAMESPACE(from_string_base2)(bigint* b, - const char* str, - size_t len); -static int BIGINT_NAMESPACE(from_string_base0)(bigint* b, - const char* str, - size_t len); - -/* returns the length of a string for the given base */ -static size_t BIGINT_NAMESPACE(len_string_base16)(const bigint* b); -static size_t BIGINT_NAMESPACE(len_string_base10)(const bigint* b); -static size_t BIGINT_NAMESPACE(len_string_base8)(const bigint* b); -static size_t BIGINT_NAMESPACE(len_string_base2)(const bigint* b); - -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base16)(char* str, - size_t len, - bigint* b); -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base10)(char* str, - size_t len, - bigint* b); -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base8)(char* str, - size_t len, - bigint* b); -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base2)(char* str, - size_t len, - bigint* b); - -static void BIGINT_NAMESPACE(reset)(bigint* b) -{ - b->size = 0; - b->sign = 0; -#ifndef BIGINT_NO_MALLOC - if (b->alloc) -#endif - b->words[0] = 0; -} - -static int BIGINT_NAMESPACE(resize)(bigint* b, size_t size) -{ -#ifdef BIGINT_NO_MALLOC - size_t alloc = size; - if (alloc * BIGINT_WORD_SIZE > BIGINT_DEFAULT_LIMIT) - return BIGINT_ENOMEM; -#else - size_t alloc = ((size + BIGINT_BLOCK_SIZE - 1)) & -BIGINT_BLOCK_SIZE; -#endif - - if (alloc * BIGINT_WORD_SIZE > b->limit) - return BIGINT_ELIMIT; - -#ifndef BIGINT_NO_MALLOC - if (b->alloc < alloc) { - if (b->words != NULL) { - b->words = (bigint_word*)realloc(b->words, alloc * BIGINT_WORD_SIZE); - } else { - b->words = (bigint_word*)malloc(alloc * BIGINT_WORD_SIZE); - } - if (b->words == NULL) - return BIGINT_ENOMEM; - memset(&b->words[b->alloc], 0, (alloc - b->alloc) * BIGINT_WORD_SIZE); - b->alloc = alloc; - } -#endif - - if (b->size > size) { /* shrinking */ - b->words[size] = 0; - } else if (b->size < size) { /* growing */ - b->words[b->size] = 0; - } - - b->size = size; - return 0; -} - -static inline void BIGINT_NAMESPACE(truncate)(bigint* b) -{ - while (b->size && b->words[b->size - 1] == 0) - b->size--; - b->sign = b->size && b->sign; -} - -static inline size_t BIGINT_NAMESPACE(word_bitlength)(bigint_word a) -{ -#if defined(_WIN32) && BIGINT_WORD_WIDTH <= 4 - unsigned long index; - unsigned long m = (unsigned long)a; - _BitScanReverse(&index, m); - return (size_t)++index; -#elif defined(_WIN64) && BIGINT_WORD_WIDTH == 8 - unsigned long index; - _BitScanReverse64(&index, a); - return (size_t)++index; -#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) - assert(a); -#if BIGINT_WORD_WIDTH == 8 - return (size_t)((sizeof(unsigned long long) * CHAR_BIT) - - __builtin_clzll((unsigned long long)a)); -#else - return (size_t)((sizeof(unsigned long) * CHAR_BIT) - - __builtin_clzl((unsigned long)a)); -#endif /* BIGINT_WORD_WIDTH == 8 */ - -#else /* all other platforms */ - size_t i = BIGINT_WORD_BIT; - - assert(a); - while (i-- > 0) { - if (a >> i & 1) - return i + 1; - } - - BIGINT_UNREACHABLE_RETURN(0); -#endif -} - -static inline int BIGINT_NAMESPACE(get_bit)(const bigint* a, size_t b) -{ - size_t word = b / BIGINT_WORD_BIT; - size_t bit = b % BIGINT_WORD_BIT; - if (word > a->size) - return 0; - return (a->words[word] >> bit) & 1; -} - -static inline int BIGINT_NAMESPACE(set_bit)(bigint* a, size_t b, uint8_t val) -{ - size_t word = b / BIGINT_WORD_BIT; - size_t bit = b % BIGINT_WORD_BIT; - int r; - - if (a->size <= word) { - if ((r = BIGINT_NAMESPACE(resize)(a, word + 1)) != 0) - return r; - } - -#if 0 /* this is the obvious way, for reference */ - if(val) { - a->words[word] |= ((bigint_word)1) << bit; - } else { - a->words[word] &= ~(((bigint_word)1) << bit); - } -#else - /* adopted from https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */ - a->words[word] ^= (-val ^ a->words[word]) & (((bigint_word)1) << bit); -#endif - return 0; -} - -static size_t BIGINT_NAMESPACE(bitlength)(const bigint* a) -{ - size_t l; - if (a->size == 0) - return 0; - l = a->size - 1; - return BIGINT_NAMESPACE(word_bitlength)(a->words[l]) + (l * BIGINT_WORD_BIT); -} - -static int BIGINT_NAMESPACE(append)(bigint* b, bigint_word val) -{ - int r; - size_t old_size = b->size; - r = BIGINT_NAMESPACE(resize)(b, b->size + 1); - if (r) - return r; - b->words[old_size] = val; - return 0; -} - -static int BIGINT_NAMESPACE(word_add)(bigint_word* a, bigint_word b) -{ - return (*a += b) < b; -} - -static int BIGINT_NAMESPACE(word_sub)(bigint_word* a, bigint_word b) -{ - bigint_word tmp = *a; - return (*a -= b) > tmp; -} - -/* performs *a *= b and returns the carry, using - * only the single-word data type */ -static inline bigint_word BIGINT_NAMESPACE(word_mul_cross)(bigint_word* a, - bigint_word b) -{ - /* assuming a 32-bit word size - - * using cross-products we can say: - * (64-bit result) = - * a_lo * bi_lo - * + (a_hi * b_lo << 16) - * + (a_lo * b_hi << 16) - * + (a_hi * b_hi << 32) - * - * when doing additions, we need to mind overflow - * and carry into res_hi. - * It's way easier and faster to just use a double-width - * type, this variant was mostly a learning exercise in how - * to multiply numbers with carry/overflow */ - - bigint_word a_hi, b_hi, a_lo, b_lo, tmp, res_lo, res_hi; - a_hi = (*a) >> BIGINT_HALF_WORD_BIT; - a_lo = (*a) & BIGINT_HALF_WORD_MASK; - b_hi = b >> BIGINT_HALF_WORD_BIT; - b_lo = b & BIGINT_HALF_WORD_MASK; - - res_lo = a_lo * b_lo; - res_hi = a_hi * b_hi; - - tmp = a_hi * b_lo; - res_hi += BIGINT_NAMESPACE(word_add)(&res_lo, tmp << BIGINT_HALF_WORD_BIT); - res_hi += (tmp >> BIGINT_HALF_WORD_BIT); - - tmp = a_lo * b_hi; - res_hi += BIGINT_NAMESPACE(word_add)(&res_lo, tmp << BIGINT_HALF_WORD_BIT); - res_hi += (tmp >> BIGINT_HALF_WORD_BIT); - - *a = res_lo; - return res_hi; -} - -#ifndef BIGINT_SINGLE_WORD_ONLY -/* attempt to find method for multiplication with a larger type */ - -#ifdef BIGINT_DWORD_TYPE -static inline bigint_word BIGINT_NAMESPACE(word_mul_double)(bigint_word* a, - bigint_word b) -{ - BIGINT_DWORD_TYPE res; - res = *a; - res *= b; - *a = res & BIGINT_WORD_MASK; - res >>= BIGINT_WORD_BIT; - return res; -} -#define BIGINT_WORD_MUL_DOUBLE -#elif BIGINT_WORD_WIDTH == 8 && defined(_WIN64) -static inline bigint_word BIGINT_NAMESPACE(word_mul_double)(bigint_word* a, - bigint_word b) -{ - uint64_t lowProduct; - uint64_t highProduct; - - lowProduct = _umul128(*a, b, &highProduct); - - *a = lowProduct; - return highProduct; -} -#define BIGINT_WORD_MUL_DOUBLE -#endif -#endif - -/* performs *a *= b and returns the carry */ -static bigint_word BIGINT_NAMESPACE(word_mul)(bigint_word* a, bigint_word b) -{ -#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_WORD_MUL_DOUBLE) - return BIGINT_NAMESPACE(word_mul_cross)(a, b); -#else /* !SINGLE_WORD_ONLY */ - return BIGINT_NAMESPACE(word_mul_double)(a, b); -#endif -} - -#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_WORD_MUL_DOUBLE) -#define BIGINT_WORD_MUL_CONST(x) \ - static inline bigint_word bigint_word_mul_const_##x(bigint_word* a) \ - { \ - bigint_word a_hi, b_hi, a_lo, b_lo, tmp, res_lo, res_hi; \ - a_hi = (*a) >> BIGINT_HALF_WORD_BIT; \ - a_lo = (*a) & BIGINT_HALF_WORD_MASK; \ - b_hi = ((bigint_word)x) >> BIGINT_HALF_WORD_BIT; \ - b_lo = ((bigint_word)x) & BIGINT_HALF_WORD_MASK; \ - res_lo = a_lo * b_lo; \ - res_hi = a_hi * b_hi; \ - tmp = a_hi * b_lo; \ - res_hi += bigint_word_add(&res_lo, tmp << BIGINT_HALF_WORD_BIT); \ - res_hi += (tmp >> BIGINT_HALF_WORD_BIT); \ - tmp = a_lo * b_hi; \ - res_hi += bigint_word_add(&res_lo, tmp << BIGINT_HALF_WORD_BIT); \ - res_hi += (tmp >> BIGINT_HALF_WORD_BIT); \ - *a = res_lo; \ - return res_hi; \ - } -#elif defined(BIGINT_DWORD_TYPE) -#define BIGINT_WORD_MUL_CONST(x) \ - static inline bigint_word bigint_word_mul_const_##x(bigint_word* a) \ - { \ - BIGINT_DWORD_TYPE res = *a; \ - res *= x; \ - *a = res & BIGINT_WORD_MASK; \ - res >>= BIGINT_WORD_BIT; \ - return res; \ - } -#else -#define BIGINT_WORD_MUL_CONST(x) \ - static inline bigint_word bigint_word_mul_const_##x(bigint_word* a) \ - { \ - return bigint_word_mul_double(a, x); -} -#endif - -BIGINT_WORD_MUL_CONST(10) - -static int BIGINT_NAMESPACE(add_word)(bigint* a, bigint_word val) -{ - int r; - size_t i = 0; - bigint_word carry = 0; - - if (!val) - return 0; /* adding 0 */ - if (!a->size) { - if ((r = BIGINT_NAMESPACE(resize)(a, 1)) != 0) - return r; - } - - carry = BIGINT_NAMESPACE(word_add)(&a->words[0], val); - - i = 1; - while (i < a->size && carry) { - carry = BIGINT_NAMESPACE(word_add)(&a->words[i], carry); - i++; - } - if (carry) { - if ((r = BIGINT_NAMESPACE(append)(a, carry)) != 0) - return r; - } - - BIGINT_NAMESPACE(truncate)(a); - return 0; -} - -static int BIGINT_NAMESPACE(add_unsigned)(bigint* a, const bigint* b) -{ - int r; - size_t i = 0; - size_t a_size, b_size, max_size; - bigint_word carry = 0; - - if (b->size == 0) - return 0; - - a_size = a->size; - b_size = b->size; - max_size = a_size > b_size ? a_size : b_size; - - if ((r = BIGINT_NAMESPACE(resize)(a, max_size)) != 0) - return r; - - i = 0; - while (i < b->size) { - carry = BIGINT_NAMESPACE(word_add)(&a->words[i], carry); - carry += BIGINT_NAMESPACE(word_add)(&a->words[i], b->words[i]); - i++; - } - while (i < max_size && carry) { - carry = BIGINT_NAMESPACE(word_add)(&a->words[i], carry); - i++; - } - if (carry) { - if ((r = BIGINT_NAMESPACE(append)(a, carry)) != 0) - return r; - } - - BIGINT_NAMESPACE(truncate)(a); - return 0; -} - -static int BIGINT_NAMESPACE(sub_unsigned)(bigint* a, const bigint* b) -{ - size_t i; - bigint_word carry = 0; - - if (b->size == 0) - return 0; - - i = 0; - while (i < b->size) { - carry = BIGINT_NAMESPACE(word_sub)(&a->words[i], carry); - carry += BIGINT_NAMESPACE(word_sub)(&a->words[i], b->words[i]); - i++; - } - while (i < a->size && carry) { - carry = BIGINT_NAMESPACE(word_sub)(&a->words[i], carry); - i++; - } - BIGINT_NAMESPACE(truncate)(a); - return 0; -} - -static int BIGINT_NAMESPACE(cmp_abs)(const bigint* a, const bigint* b) -{ - size_t i; - if (a->size != b->size) - return a->size < b->size ? -1 : 1; - i = a->size; - while (i--) { - if (a->words[i] != b->words[i]) - return a->words[i] < b->words[i] ? -1 : 1; - } - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(lshift_overwrite)(bigint* c, size_t bits) -{ - int r; - size_t words, expanded, i; - bigint_word lo, hi; - - if (c->size == 0) - return 0; - - words = bits / BIGINT_WORD_BIT; - bits %= BIGINT_WORD_BIT; - - expanded = c->size + words + (bits != 0); - - if ((r = BIGINT_NAMESPACE(resize)(c, expanded)) != 0) - return r; - - if (bits == 0) { - i = expanded; - while (i-- > words) { - c->words[i] = c->words[i - words]; - } - } else { - lo = hi = 0; - i = expanded; - while (--i > words) { - lo = c->words[i - words - 1]; - c->words[i] = (hi << bits) | (lo >> (BIGINT_WORD_BIT - bits)); - hi = lo; - } - c->words[words] = hi << bits; - } - i = 0; - while (i < words) { - c->words[i] = 0; - i++; - } - BIGINT_NAMESPACE(truncate)(c); - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(lshift)(bigint* c, const bigint* a, size_t bits) -{ - int r; - - if ((r = BIGINT_NAMESPACE(copy)(c, a)) != 0) - return r; - return BIGINT_NAMESPACE(lshift_overwrite)(c, bits); -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(rshift_overwrite)(bigint* c, size_t bits) -{ - int r; - size_t words, i; - bigint_word lo, hi; - - words = bits / BIGINT_WORD_BIT; - if (words >= - c->size) { /* right-shiting by more words than we actually have */ - c->size = 0; - c->sign = 0; - return 0; - } - - bits %= BIGINT_WORD_BIT; - i = 0; - - if (bits == 0) { - while (i < c->size - words) { - c->words[i] = c->words[i + words]; - i++; - } - } else { - lo = hi = c->words[words]; - while (i < c->size - words - 1) { - hi = c->words[i + words + 1]; - c->words[i] = (hi << (BIGINT_WORD_BIT - bits)) | lo >> bits; - lo = hi; - i++; - } - c->words[c->size - words - 1] = lo >> bits; - } - if ((r = BIGINT_NAMESPACE(resize)(c, c->size - words)) != 0) - return r; - BIGINT_NAMESPACE(truncate)(c); - return 0; -} - -#define BIGINT_RSHIFT_OVERWRITE_SMALL(x) \ - static inline void bigint_rshift_overwrite_small_##x(bigint* c) \ - { \ - size_t i; \ - bigint_word lo, hi; \ - i = 0; \ - lo = hi = c->words[0]; \ - while (i < c->size - 1) { \ - hi = c->words[i + 1]; \ - c->words[i] = (hi << (BIGINT_WORD_BIT - x)) | lo >> x; \ - lo = hi; \ - i++; \ - } \ - c->words[i] = lo >> x; \ - bigint_truncate(c); \ - } - -BIGINT_RSHIFT_OVERWRITE_SMALL(4) -BIGINT_RSHIFT_OVERWRITE_SMALL(3) -BIGINT_RSHIFT_OVERWRITE_SMALL(1) - -BIGINT_FUNC -int BIGINT_NAMESPACE(rshift)(bigint* c, const bigint* a, size_t bits) -{ - int r; - if ((r = BIGINT_NAMESPACE(copy)(c, a)) != 0) - return r; - return BIGINT_NAMESPACE(rshift_overwrite)(c, bits); -} - -static int BIGINT_NAMESPACE(mul_long)(bigint* c, - const bigint* a, - const bigint* b) -{ - int r; - size_t expanded; - size_t ia, ib; - bigint_word carry; -#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_DWORD_TYPE) - bigint_word lo, hi; -#else - BIGINT_DWORD_TYPE res; -#endif - - expanded = a->size + b->size + 1; - - if ((r = BIGINT_NAMESPACE(resize)(c, expanded)) != 0) - return r; - c->sign = a->sign ^ b->sign; - - for (ib = 0; ib < b->size; ib++) { - carry = 0; - for (ia = 0; ia < a->size; ia++) { -#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_DWORD_TYPE) - lo = a->words[ia]; - hi = BIGINT_NAMESPACE(word_mul)(&lo, b->words[ib]); - hi += BIGINT_NAMESPACE(word_add)(&lo, c->words[ia + ib]); - hi += BIGINT_NAMESPACE(word_add)(&lo, carry); - c->words[ia + ib] = lo; - carry = hi; -#else - res = a->words[ia]; - res *= b->words[ib]; - res += c->words[ia + ib]; - res += carry; - c->words[ia + ib] = res & BIGINT_WORD_MASK; - carry = res >> BIGINT_WORD_BIT; -#endif - } - c->words[ib + a->size] = carry; - } - - BIGINT_NAMESPACE(truncate)(c); - return 0; -} - -static int BIGINT_NAMESPACE(mul_word)(bigint* a, bigint_word val) -{ - int r; - size_t i = 0; - bigint_word carry = 0; - bigint_word c = 0; - - if (!val) { - a->size = 0; - a->sign = 0; - return 0; - } - - carry = 0; - for (i = 0; i < a->size; i++) { - c = BIGINT_NAMESPACE(word_mul)(&a->words[i], val); - c += BIGINT_NAMESPACE(word_add)(&a->words[i], carry); - carry = c; - } - if (carry) { - if ((r = BIGINT_NAMESPACE(append)(a, carry)) != 0) - return r; - } - - BIGINT_NAMESPACE(truncate)(a); - return 0; -} - -#define BIGINT_MUL_CONST(x) \ - static inline int bigint_mul_const_##x(bigint* a) \ - { \ - int r; \ - size_t i = 0; \ - bigint_word carry = 0; \ - bigint_word c = 0; \ - for (i = 0; i < a->size; i++) { \ - c = bigint_word_mul_const_##x(&a->words[i]); \ - c += bigint_word_add(&a->words[i], carry); \ - carry = c; \ - } \ - if (carry) { \ - if ((r = bigint_append(a, carry)) != 0) \ - return r; \ - } \ - bigint_truncate(a); \ - return 0; \ - } - -BIGINT_MUL_CONST(10) - -/* does NOT accept a string with a leading 0x - that's handled in - * bigint_from_string. does NOT parse a leading +/-, also handled - * in bigint_from_string */ -static int BIGINT_NAMESPACE(from_string_base16)(bigint* b, - const char* str, - size_t len) -{ - bigint_word word; - size_t i = 0; - int r; - - while (i < len && str[i]) { - if ((r = BIGINT_NAMESPACE(lshift_overwrite)(b, 4)) != 0) - return r; - switch (str[i]) { - case '0': - word = 0; - break; - case '1': - word = 1; - break; - case '2': - word = 2; - break; - case '3': - word = 3; - break; - case '4': - word = 4; - break; - case '5': - word = 5; - break; - case '6': - word = 6; - break; - case '7': - word = 7; - break; - case '8': - word = 8; - break; - case '9': - word = 9; - break; - case 'A': - word = 10; - break; - case 'a': - word = 10; - break; - case 'B': - word = 11; - break; - case 'b': - word = 11; - break; - case 'C': - word = 12; - break; - case 'c': - word = 12; - break; - case 'D': - word = 13; - break; - case 'd': - word = 13; - break; - case 'E': - word = 14; - break; - case 'e': - word = 14; - break; - case 'F': - word = 15; - break; - case 'f': - word = 15; - break; - default: - return BIGINT_EINVAL; - } - if ((r = BIGINT_NAMESPACE(add_word)(b, word)) != 0) - return r; - i++; - } - - return 0; -} - -/* does NOT parse a leading +/-, handled in bigint_from_string */ -static int BIGINT_NAMESPACE(from_string_base10)(bigint* b, - const char* str, - size_t len) -{ - bigint_word word; - size_t i = 0; - int r; - - while (i < len && str[i]) { - if ((r = BIGINT_NAMESPACE(mul_const_10)(b)) != 0) - return r; - switch (str[i]) { - case '0': - word = 0; - break; - case '1': - word = 1; - break; - case '2': - word = 2; - break; - case '3': - word = 3; - break; - case '4': - word = 4; - break; - case '5': - word = 5; - break; - case '6': - word = 6; - break; - case '7': - word = 7; - break; - case '8': - word = 8; - break; - case '9': - word = 9; - break; - default: - return BIGINT_EINVAL; - } - if ((r = BIGINT_NAMESPACE(add_word)(b, word)) != 0) - return r; - i++; - } - - return 0; -} - -/* does NOT parse a leading +/-, handled in bigint_from_string */ -static int BIGINT_NAMESPACE(from_string_base8)(bigint* b, - const char* str, - size_t len) -{ - bigint_word word; - size_t i = 0; - int r; - - while (i < len && str[i]) { - if ((r = BIGINT_NAMESPACE(lshift_overwrite)(b, 3)) != 0) - return r; - switch (str[i]) { - case '0': - word = 0; - break; - case '1': - word = 1; - break; - case '2': - word = 2; - break; - case '3': - word = 3; - break; - case '4': - word = 4; - break; - case '5': - word = 5; - break; - case '6': - word = 6; - break; - case '7': - word = 7; - break; - default: - return BIGINT_EINVAL; - } - if ((r = BIGINT_NAMESPACE(add_word)(b, word)) != 0) - return r; - i++; - } - - return 0; -} - -/* does NOT parse a leading +/-, handled in bigint_from_string */ -static int BIGINT_NAMESPACE(from_string_base2)(bigint* b, - const char* str, - size_t len) -{ - bigint_word word; - size_t i = 0; - int r; - - while (i < len && str[i]) { - if ((r = BIGINT_NAMESPACE(lshift_overwrite)(b, 1)) != 0) - return r; - switch (str[i]) { - case '0': - word = 0; - break; - case '1': - word = 1; - break; - default: - return BIGINT_EINVAL; - } - if ((r = BIGINT_NAMESPACE(add_word)(b, word)) != 0) - return r; - i++; - } - - return 0; -} - -static int BIGINT_NAMESPACE(from_string_base0)(bigint* b, - const char* str, - size_t len) -{ - if (str[0] == '0') { - str++; - len--; - if (len == 0) { - BIGINT_NAMESPACE(reset)(b); - return 0; - } - - if (str[0] == 'x' || str[0] == 'X') { - str++; - len--; - if (len == 0) - return BIGINT_EINVAL; - return BIGINT_NAMESPACE(from_string_base16)(b, str, len); - } - - if (str[0] == 'b' || str[0] == 'B') { - str++; - len--; - if (len == 0) - return BIGINT_EINVAL; - return BIGINT_NAMESPACE(from_string_base2)(b, str, len); - } - - return BIGINT_NAMESPACE(from_string_base8)(b, str, len); - } - - if (len > 1 && str[0] == 'x') { - str++; - len--; - return BIGINT_NAMESPACE(from_string_base16)(b, str, len); - } - - if (len > 1 && str[0] == 'b') { - str++; - len--; - return BIGINT_NAMESPACE(from_string_base2)(b, str, len); - } - - return BIGINT_NAMESPACE(from_string_base10)(b, str, len); -} - -/* allows strings like: - * -b010101 - * -0b010101 - * 01231 (octal) - * 1234 (base10) - * x1234 (base16) - * 0x1234 (base16) - */ - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_string)(bigint* b, - const char* str, - size_t len, - unsigned int base) -{ - int r = 0; - size_t sign = 0; - - BIGINT_NAMESPACE(reset)(b); - - if (len == 0) - return BIGINT_EINVAL; - - if (str[0] == '-') { - sign = 1; - str++; - len--; - if (len == 0) - return BIGINT_EINVAL; - } - - switch (base) { - case 2: { - if (len > 1 && (str[0] == 'b')) { - str++; - len--; - if (len == 0) - return BIGINT_EINVAL; - } else if (len > 2 && str[0] == '0' && (str[1] == 'b' || str[1] == 'B')) { - str += 2; - len -= 2; - } - r = BIGINT_NAMESPACE(from_string_base2)(b, str, len); - break; - } - case 8: { - r = BIGINT_NAMESPACE(from_string_base8)(b, str, len); - break; - } - case 10: - r = BIGINT_NAMESPACE(from_string_base10)(b, str, len); - break; - case 16: { - if (len > 1 && str[0] == 'x') { - str++; - len--; - if (len == 0) - return BIGINT_EINVAL; - } else if (len > 2 && str[0] == '0' && (str[1] == 'X' || str[1] == 'x')) { - str += 2; - len -= 2; - } - r = BIGINT_NAMESPACE(from_string_base16)(b, str, len); - break; - } - case 0: - r = BIGINT_NAMESPACE(from_string_base0)(b, str, len); - break; - default: - return BIGINT_EINVAL; - } - if (r == 0) { - b->sign = sign; - } - return r; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_cstring)(bigint* b, - const char* str, - unsigned int base) -{ - return BIGINT_NAMESPACE(from_string)(b, str, strlen(str), base); -} - -BIGINT_FUNC -void BIGINT_NAMESPACE(init)(bigint* b) -{ - *b = *BIGINT_ZERO; - b->limit = BIGINT_DEFAULT_LIMIT; -} - -BIGINT_FUNC -void BIGINT_NAMESPACE(free)(bigint* b) -{ - BIGINT_NAMESPACE(reset)(b); -#ifndef BIGINT_NO_MALLOC - if (b->alloc != 0) { - free(b->words); - b->words = NULL; - b->alloc = 0; - } -#endif -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(copy)(bigint* dest, const bigint* src) -{ - int r; - size_t limit; - if (src == dest) - return 0; - limit = dest->limit; - dest->limit = src->limit; - if (src->size != dest->size) { - r = BIGINT_NAMESPACE(resize)(dest, src->size); - if (r) { - dest->limit = limit; - return r; - } - } - - /* we may be copying a zero-length */ - if (dest->size == 0) { - dest->sign = 0; - return 0; - } - - assert(dest->words != NULL); - memcpy(dest->words, src->words, dest->size * BIGINT_WORD_SIZE); - dest->sign = src->sign; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_word)(bigint* b, bigint_word val) -{ - BIGINT_NAMESPACE(reset)(b); - if (!val) - return 0; - return BIGINT_NAMESPACE(append)(b, val); -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u8)(bigint* b, uint8_t val) -{ - BIGINT_NAMESPACE(reset)(b); - if (!val) - return 0; - return BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u16)(bigint* b, uint16_t val) -{ - int r; - BIGINT_NAMESPACE(reset)(b); - if (!val) - return 0; -#if BIGINT_WORD_WIDTH < 2 - while (val) { - r = BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); - if (r) - return r; - val >>= BIGINT_WORD_BIT; - } -#else - r = BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); -#endif - return r; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u32)(bigint* b, uint32_t val) -{ - int r; - BIGINT_NAMESPACE(reset)(b); - if (!val) - return 0; -#if BIGINT_WORD_WIDTH < 4 - while (val) { - r = BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); - if (r) - return r; - val >>= BIGINT_WORD_BIT; - } -#else - r = BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); -#endif - return r; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_u64)(bigint* b, uint64_t val) -{ - int r; - BIGINT_NAMESPACE(reset)(b); - if (!val) - return 0; -#if BIGINT_WORD_WIDTH < 8 - while (val) { - r = BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); - if (r) - return r; - val >>= BIGINT_WORD_BIT; - } -#else - r = BIGINT_NAMESPACE(append)( - b, (BIGINT_NAMESPACE(word))(val & BIGINT_WORD_MASK)); -#endif - return r; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i8)(bigint* b, int8_t val) -{ - int r; - uint8_t uval = val < 0 ? -val : val; - - r = BIGINT_NAMESPACE(from_u8)(b, uval); - if (r) - return r; - b->sign = val < 0; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i16)(bigint* b, int16_t val) -{ - int r; - uint16_t uval = val < 0 ? -val : val; - - r = BIGINT_NAMESPACE(from_u16)(b, uval); - if (r) - return r; - b->sign = val < 0; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i32)(bigint* b, int32_t val) -{ - int r; - uint32_t uval = val < 0 ? -val : val; - - r = BIGINT_NAMESPACE(from_u32)(b, uval); - if (r) - return r; - b->sign = val < 0; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(from_i64)(bigint* b, int64_t val) -{ - int r; - uint64_t uval = val < 0 ? -val : val; - - r = BIGINT_NAMESPACE(from_u64)(b, uval); - if (r) - return r; - b->sign = val < 0; - return 0; -} - -static inline uint64_t BIGINT_NAMESPACE(to_u64_internal)(const bigint* b) -{ - uint64_t tmp = 0; - size_t i = b->size; - -#if BIGINT_WORD_WIDTH == 8 - if (i) - tmp = b->words[0]; -#else - while (i--) { - tmp <<= BIGINT_WORD_BIT; - tmp += b->words[i] & BIGINT_WORD_MASK; - } -#endif - - return tmp; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u8)(uint8_t* val, const bigint* b) -{ - uint64_t tmp = 0; - - if (BIGINT_NAMESPACE(bitlength)(b) > 8) - return 1; - if (b->size) { - tmp = BIGINT_NAMESPACE(to_u64_internal)(b); - } - *val = (uint8_t)tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i8)(int8_t* val, const bigint* b) -{ - uint8_t tmp = 0; - uint8_t max = INT8_MAX; - int r = 0; - - if ((r = BIGINT_NAMESPACE(to_u8)(&tmp, b)) != 0) - return r; - max += !!b->sign; - if (tmp > max) - return 1; - - *val = b->sign ? -tmp : tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u16)(uint16_t* val, const bigint* b) -{ - uint64_t tmp = 0; - - if (BIGINT_NAMESPACE(bitlength)(b) > 16) - return 1; - if (b->size) { - tmp = BIGINT_NAMESPACE(to_u64_internal)(b); - } - *val = (uint16_t)tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i16)(int16_t* val, const bigint* b) -{ - uint16_t tmp = 0; - uint16_t max = INT16_MAX; - int r = 0; - - if ((r = BIGINT_NAMESPACE(to_u16)(&tmp, b)) != 0) - return r; - max += !!b->sign; - if (tmp > max) - return 1; - - *val = b->sign ? -tmp : tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u32)(uint32_t* val, const bigint* b) -{ - uint64_t tmp = 0; - - if (BIGINT_NAMESPACE(bitlength)(b) > 32) - return 1; - if (b->size) { - tmp = BIGINT_NAMESPACE(to_u64_internal)(b); - } - *val = (uint32_t)tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i32)(int32_t* val, const bigint* b) -{ - uint32_t tmp = 0; - uint32_t max = INT32_MAX; - int r = 0; - - if ((r = BIGINT_NAMESPACE(to_u32)(&tmp, b)) != 0) - return r; - max += !!b->sign; - if (tmp > max) - return 1; - - *val = b->sign ? -tmp : tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_u64)(uint64_t* val, const bigint* b) -{ - uint64_t tmp = 0; - - if (BIGINT_NAMESPACE(bitlength)(b) > 64) - return 1; - if (b->size) { - tmp = BIGINT_NAMESPACE(to_u64_internal)(b); - } - *val = (uint64_t)tmp; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(to_i64)(int64_t* val, const bigint* b) -{ - uint64_t tmp = 0; - uint64_t max = INT64_MAX; - int r = 0; - - if ((r = BIGINT_NAMESPACE(to_u64)(&tmp, b)) != 0) - return r; - max += !!b->sign; - if (tmp > max) - return 1; - - *val = b->sign ? -tmp : tmp; - return 0; -} - -static int BIGINT_NAMESPACE(add_signed)(bigint* c, - const bigint* a, - size_t a_sign, - const bigint* b, - size_t b_sign) -{ - int r; - int cmp; - - if (a_sign == b_sign) { - if ((r = BIGINT_NAMESPACE(copy)(c, a)) != 0) - return r; - if ((r = BIGINT_NAMESPACE(add_unsigned)(c, b)) != 0) - return r; - c->sign = a_sign; - return 0; - } - - cmp = BIGINT_NAMESPACE(cmp_abs)(a, b); - - if (cmp == 0) { - /* adding positive and negative numbers with the same - * value, short-circuit to returning zero */ - c->size = 0; - c->sign = 0; - return 0; - } - - if (cmp > 0) { - if ((r = BIGINT_NAMESPACE(copy)(c, a)) != 0) - return r; - if ((r = BIGINT_NAMESPACE(sub_unsigned)(c, b)) != 0) - return r; - c->sign = a_sign; - return 0; - } - - if ((r = BIGINT_NAMESPACE(copy)(c, b)) != 0) - return r; - if ((r = BIGINT_NAMESPACE(sub_unsigned)(c, a)) != 0) - return r; - c->sign = b_sign; - return 0; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(add)(bigint* c, const bigint* a, const bigint* b) -{ - int r; - bigint tmp = BIGINT_INIT; - - if ((r = BIGINT_NAMESPACE(copy)(&tmp, c)) != 0) - return r; - if ((r = BIGINT_NAMESPACE(add_signed)(&tmp, a, a->sign, b, b->sign)) != 0) - goto cleanup; - if ((r = BIGINT_NAMESPACE(copy)(c, &tmp)) != 0) - goto cleanup; - -cleanup: - BIGINT_NAMESPACE(free)(&tmp); - return r; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(sub)(bigint* c, const bigint* a, const bigint* b) -{ - int r; - bigint tmp = BIGINT_INIT; - - if ((r = BIGINT_NAMESPACE(copy)(&tmp, c)) != 0) - return r; - if ((r = BIGINT_NAMESPACE(add_signed)(&tmp, a, a->sign, b, !b->sign)) != 0) - goto cleanup; - if ((r = BIGINT_NAMESPACE(copy)(c, &tmp)) != 0) - goto cleanup; - -cleanup: - BIGINT_NAMESPACE(free)(&tmp); - return r; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(mul)(bigint* c, const bigint* a, const bigint* b) -{ - int r; - bigint tmp = BIGINT_INIT; - - if ((r = BIGINT_NAMESPACE(copy)(&tmp, c)) != 0) - return r; - if ((r = BIGINT_NAMESPACE(mul_long)(&tmp, a, b)) != 0) - goto cleanup; - if ((r = BIGINT_NAMESPACE(copy)(c, &tmp)) != 0) - goto cleanup; - -cleanup: - BIGINT_NAMESPACE(free)(&tmp); - return r; -} - -BIGINT_FUNC -void BIGINT_NAMESPACE(div_mod_word)(bigint* numerator, - bigint_word* remainder, - bigint_word denominator) -{ - size_t i; - size_t j; - - bigint_word dst_word; - bigint_word src_word; - bigint_word parts[2]; - bigint_word r = 0; - - bigint_word div_word; - bigint_word mod_word; - - i = numerator->size; - - while (i-- > 0) { - dst_word = 0; - src_word = numerator->words[i]; - - parts[0] = src_word >> BIGINT_HALF_WORD_BIT; - parts[1] = src_word & BIGINT_HALF_WORD_MASK; - - for (j = 0; j < 2; j++) { - r <<= BIGINT_HALF_WORD_BIT; - r |= parts[j]; - - div_word = r / denominator; - mod_word = r % denominator; - r = mod_word; - - dst_word <<= BIGINT_HALF_WORD_BIT; - dst_word |= div_word; - } - - numerator->words[i] = dst_word; - } - - BIGINT_NAMESPACE(truncate)(numerator); - *remainder = r; -} - -/* div_mod_word hard-coded to some value to allow compiler-time optimizations */ -#define BIGINT_DIV_MOD_WORD(x) \ - static inline void bigint_div_mod_##x(bigint* numerator, \ - bigint_word* remainder) \ - { \ - size_t i; \ - size_t j; \ - bigint_word dst_word; \ - bigint_word src_word; \ - bigint_word parts[2]; \ - bigint_word r = 0; \ - bigint_word div_word; \ - bigint_word mod_word; \ - i = numerator->size; \ - while (i-- > 0) { \ - dst_word = 0; \ - src_word = numerator->words[i]; \ - parts[0] = src_word >> BIGINT_HALF_WORD_BIT; \ - parts[1] = src_word & BIGINT_HALF_WORD_MASK; \ - for (j = 0; j < 2; j++) { \ - r <<= BIGINT_HALF_WORD_BIT; \ - r |= parts[j]; \ - div_word = r / x; \ - mod_word = r % x; \ - r = mod_word; \ - dst_word <<= BIGINT_HALF_WORD_BIT; \ - dst_word |= div_word; \ - } \ - numerator->words[i] = dst_word; \ - } \ - bigint_truncate(numerator); \ - *remainder = r; \ - } - -BIGINT_DIV_MOD_WORD(10) - -BIGINT_FUNC -int BIGINT_NAMESPACE(div_mod)(bigint* quotient, - bigint* remainder, - const bigint* numerator, - const bigint* denominator) -{ - int r; - size_t n; - - bigint quo = BIGINT_INIT; - bigint rem = BIGINT_INIT; - bigint den = BIGINT_INIT; - - if ((r = BIGINT_NAMESPACE(copy)(&rem, numerator)) != 0) - goto cleanup; - if ((r = BIGINT_NAMESPACE(copy)(&den, denominator)) != 0) - goto cleanup; - - if (BIGINT_NAMESPACE(cmp_abs)(&rem, denominator) >= 0) { - n = BIGINT_NAMESPACE(bitlength)(numerator) - - BIGINT_NAMESPACE(bitlength)(denominator); - if ((r = BIGINT_NAMESPACE(lshift)(&den, denominator, n)) != 0) - goto cleanup; - n++; /* ensure we have at least 1 iteration */ - while (n--) { - if (BIGINT_NAMESPACE(cmp_abs)(&rem, &den) >= 0) { - if ((r = BIGINT_NAMESPACE(sub_unsigned)(&rem, &den)) != 0) - goto cleanup; - if ((r = BIGINT_NAMESPACE(set_bit)(&quo, (size_t)n, 1)) != 0) - goto cleanup; - } - if ((r = BIGINT_NAMESPACE(rshift_overwrite)(&den, 1)) != 0) - goto cleanup; - } - } - - quo.sign = numerator->sign ^ denominator->sign; - rem.sign = numerator->sign; - - if ((r = BIGINT_NAMESPACE(copy)(quotient, &quo)) != 0) - goto cleanup; - if ((r = BIGINT_NAMESPACE(copy)(remainder, &rem)) != 0) - goto cleanup; - -cleanup: - BIGINT_NAMESPACE(free)(&quo); - BIGINT_NAMESPACE(free)(&rem); - BIGINT_NAMESPACE(free)(&den); - return r; -} - -static const bigint_word BIGINT_ONE_VALUE = 1; -#ifdef BIGINT_NO_MALLOC -static const bigint BIGINT_ONE_STORAGE = {.words[0] = BIGINT_ONE_VALUE, - .size = 1, - .alloc = 0, - .sign = 0, - .limit = BIGINT_DEFAULT_LIMIT}; -#else -static const bigint BIGINT_ONE_STORAGE = { - .words = (bigint_word*)&BIGINT_ONE_VALUE, - .size = 1, - .alloc = 0, - .sign = 0, - .limit = BIGINT_DEFAULT_LIMIT}; -#endif -static const bigint* BIGINT_ONE = &BIGINT_ONE_STORAGE; - -BIGINT_FUNC -int BIGINT_NAMESPACE(inc)(bigint* c, const bigint* a) -{ - return BIGINT_NAMESPACE(add)(c, a, BIGINT_ONE); -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(dec)(bigint* c, const bigint* a) -{ - return BIGINT_NAMESPACE(sub)(c, a, BIGINT_ONE); -} - -/* we don't take the sign into account on these functions, - * that's handled in the bigint_to_string function */ -static size_t BIGINT_NAMESPACE(len_string_base16)(const bigint* b) -{ - size_t blen = BIGINT_NAMESPACE(bitlength)(b); - - return (blen >> 2) + (!!(blen & 0x03)); -} - -/* pre-computed 1.0 / log2(10.0); */ -static const double base10_scale = 0x1.34413509f79ffp-2; -static size_t BIGINT_NAMESPACE(len_string_base10)(const bigint* b) -{ - size_t blen = - (size_t)(((double)BIGINT_NAMESPACE(bitlength)(b)) * base10_scale); - - return ++blen; -} - -static size_t BIGINT_NAMESPACE(len_string_base8)(const bigint* b) -{ - size_t blen = BIGINT_NAMESPACE(bitlength)(b); - - return (blen / 3) + (!!(blen % 3)); -} - -static size_t BIGINT_NAMESPACE(len_string_base2)(const bigint* b) -{ - return BIGINT_NAMESPACE(bitlength)(b); -} - -static const char* const BIGINT_NAMESPACE(alphabet) = "0123456789abcdef"; - -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base16)(char* str, - size_t len, - bigint* b) -{ - size_t u = 0; - - if (b->size == 0) { - str[0] = '0'; - return 1; - } - - while ((u = BIGINT_NAMESPACE(len_string_base16)(b)) > len) { - BIGINT_NAMESPACE(rshift_overwrite_small_4)(b); - } - len = u; - - while (b->size) { - str[--u] = BIGINT_NAMESPACE(alphabet)[b->words[0] & 0x0F]; - BIGINT_NAMESPACE(rshift_overwrite_small_4)(b); - } - - return len; -} - -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base10)(char* str, - size_t len, - bigint* b) -{ - bigint_word rem; - size_t u = 0; - - if (b->size == 0) { - str[0] = '0'; - return 1; - } - - while ((u = BIGINT_NAMESPACE(len_string_base10)(b)) > len) { - BIGINT_NAMESPACE(div_mod_10)(b, &rem); - } - len = u; - - while (b->size) { - BIGINT_NAMESPACE(div_mod_10)(b, &rem); - str[--u] = BIGINT_NAMESPACE(alphabet)[rem]; - } - - if (u) { - memmove(&str[0], &str[u], len - u); - len -= u; - } - - return len; -} - -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base8)(char* str, - size_t len, - bigint* b) -{ - size_t u = 0; - - if (b->size == 0) { - str[0] = '0'; - return 1; - } - - while ((u = BIGINT_NAMESPACE(len_string_base8)(b)) > len) { - BIGINT_NAMESPACE(rshift_overwrite_small_3)(b); - } - len = u; - - while (b->size) { - str[--u] = BIGINT_NAMESPACE(alphabet)[b->words[0] & 0x07]; - BIGINT_NAMESPACE(rshift_overwrite_small_3)(b); - } - - return len; -} - -BIGINT_NONNULL1 -static size_t BIGINT_NAMESPACE(to_string_base2)(char* str, - size_t len, - bigint* b) -{ - size_t u = 0; - - if (b->size == 0) { - str[0] = '0'; - return 1; - } - - while ((u = BIGINT_NAMESPACE(bitlength)(b)) > len) { - BIGINT_NAMESPACE(rshift_overwrite_small_1)(b); - } - len = u; - - while (b->size) { - str[--u] = BIGINT_NAMESPACE(alphabet)[b->words[0] & 0x01]; - BIGINT_NAMESPACE(rshift_overwrite_small_1)(b); - } - - return len; -} - -BIGINT_FUNC -size_t BIGINT_NAMESPACE(to_string)(char* str, - size_t len, - const bigint* b, - unsigned int base) -{ - bigint tmp = BIGINT_INIT; - int r; - size_t u = 0; - size_t res = 0; - - if (str == NULL) { - switch (base) { - case 2: - return 2 + b->sign + BIGINT_NAMESPACE(len_string_base2)(b); - case 8: - return 1 + b->sign + BIGINT_NAMESPACE(len_string_base8)(b); - case 0: /* fall-through */ - case 10: - return b->sign + BIGINT_NAMESPACE(len_string_base10)(b); - case 16: - return 2 + b->sign + BIGINT_NAMESPACE(len_string_base16)(b); - default: - break; - } - return 0; - } - - if (len == 0) - return 0; - - if (b->sign) { - *str = '-'; - str++; - len--; - u++; - } - if (len == 0) - return u; - - if (base == 2) { - *str = '0'; - str++; - len--; - u++; - if (len == 0) - return u; - *str = 'b'; - str++; - len--; - u++; - } else if (base == 8) { - *str = '0'; - str++; - len--; - u++; - } else if (base == 16) { - *str = '0'; - str++; - len--; - u++; - if (len == 0) - return u; - *str = 'x'; - str++; - len--; - u++; - } - if (len == 0) - return u; - - /* TODO can this copy be avoided? */ - if ((r = BIGINT_NAMESPACE(copy)(&tmp, b)) != 0) - return 0; - switch (base) { - case 2: - res = BIGINT_NAMESPACE(to_string_base2)(str, len, &tmp); - break; - case 8: - res = BIGINT_NAMESPACE(to_string_base8)(str, len, &tmp); - break; - case 0: /* fall-through */ - case 10: - res = BIGINT_NAMESPACE(to_string_base10)(str, len, &tmp); - break; - case 16: - res = BIGINT_NAMESPACE(to_string_base16)(str, len, &tmp); - break; - default: - break; - } - BIGINT_NAMESPACE(free)(&tmp); - - if (res != 0) - res += u; - return res; -} - -BIGINT_FUNC -int BIGINT_NAMESPACE(cmp)(const bigint* a, const bigint* b) -{ - if (!a->size && !b->size) - return 0; - if (a->sign && b->sign) - return BIGINT_NAMESPACE(cmp_abs)(b, a); - if (a->sign == b->sign) - return BIGINT_NAMESPACE(cmp_abs)(a, b); - return a->sign && !b->sign ? -1 : 1; -} - -#endif - -#ifdef BIGINT_UNDEF -#undef BIGINT_BLOCK_SIZE -#undef BIGINT_DEFAULT_LIMIT -#undef BIGINT_DIV_MOD_WORD -#undef BIGINT_DWORD_TYPE -#undef BIGINT_EINVAL -#undef BIGINT_ELIMIT -#undef BIGINT_ENOMEM -#undef bigint_eq -#undef BIGINT_FUNC -#undef bigint_ge -#undef bigint_gt -#undef BIGINT_H -#undef BIGINT_HALF_WORD_BIT -#undef BIGINT_HALF_WORD_MASK -#undef BIGINT_INIT -#undef bigint_le -#undef bigint_lt -#undef BIGINT_MUL_CONST -#undef BIGINT_NONNULL1 -#undef BIGINT_RSHIFT_OVERWRITE_SMALL -#undef BIGINT_UNREACHABLE -#undef BIGINT_UNREACHABLE_RETURN -#undef BIGINT_WORD_BIT -#undef BIGINT_WORD_MASK -#undef BIGINT_WORD_MUL_CONST -#undef BIGINT_WORD_MUL_DOUBLE -#undef BIGINT_WORD_SIZE -#undef BIGINT_WORD_TYPE -#undef BIGINT_WORD_WIDTH -#undef BIGINT_NAMESPACE -#endif - -/* -Copyright (c) 2023 John Regan - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -*/ diff --git a/poly1305.h b/poly1305.h new file mode 100644 index 0000000..24df1b3 --- /dev/null +++ b/poly1305.h @@ -0,0 +1,179 @@ +#ifndef SLOWCRYPT_POLY1305_H +#define SLOWCRYPT_POLY1305_H + +#ifndef SLOWCRYPT_POLY1305_FUNC +#define SLOWCRYPT_POLY1305_FUNC /**/ +#endif + +#ifndef SLOWCRYPT_POLY1305_DONT_USE_BITINT +#include +#ifdef BITINT_MAXWIDTH +#if BITINT_MAXWIDTH >= 264 +#define SLOWCRYPT_POLY1305_USE_BITINT +#endif +#elif defined(__BITINT_MAXWIDTH__) +#if __BITINT_MAXWIDTH__ >= 264 +#define SLOWCRYPT_POLY1305_USE_BITINT +#endif +#endif +#endif + +#ifdef SLOWCRYPT_POLY1305_USE_BITINT +#else +ERROR NOT YET IMPLEMENTED; +#ifdef SLOWCRYPT_POLY1305_IMPL +#define BIGINT_IMPLEMENTATION +#endif +#define BIGINT_FUNC SLOWCRYPT_POLY1305_FUNC +#define BIGINT_NAMESPACE(x) slowcrypt_poly1305_bigint_##x +#define BIGINT_UNDEF +#define BIGINT_NO_MALLOC +#define BIGINT_DEFAULT_LIMIT 33 +#define BIGINT_WORD_WIDTH 1 +#endif + +#include + +typedef struct +{ +#ifdef SLOWCRYPT_POLY1305_USE_BITINT + unsigned _BitInt(128) r, s; + unsigned _BitInt(136) acc; + unsigned _BitInt(264) prod; +#else + slowcrypt_poly1305_bigint_bigint r, s, acc, p, temp1, temp2; +#endif +} slowcrypt_poly1305; + +/* the key buffer will be destroyed: used as scratch buffer */ +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_init(slowcrypt_poly1305* p, + uint8_t key[32]); + +/* only the last block is allowed to be shorter than 16 bytes! + * + * length has to be maximum 16 + */ +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_next_block( + slowcrypt_poly1305* p, + uint8_t const* data, + unsigned int length); + +/* also zeroizes memory */ +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_finish(slowcrypt_poly1305* p, + uint8_t out[16]); + +#ifdef SLOWCRYPT_POLY1305_IMPL + +#ifdef SLOWCRYPT_POLY1305_USE_BITINT + +static unsigned _BitInt(136) slowcrypt_poly1305_from_le(uint8_t const* buf, + unsigned int buf_len, + uint8_t top) +{ + unsigned int i; + unsigned _BitInt(136) out = (unsigned _BitInt(136))top << (buf_len * 8); + for (i = 0; i < buf_len; i++) + out |= (unsigned _BitInt(136))buf[i] << (i * 8); + return out; +} + +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_init(slowcrypt_poly1305* p, + uint8_t key[32]) +{ + key[3] &= 15; + key[7] &= 15; + key[11] &= 15; + key[15] &= 15; + key[4] &= 252; + key[8] &= 252; + key[12] &= 252; + p->r = (unsigned _BitInt(128))slowcrypt_poly1305_from_le(key, 16, 0); + p->s = (unsigned _BitInt(128))slowcrypt_poly1305_from_le(&key[16], 16, 0); + p->acc = 0; +} + +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_next_block( + slowcrypt_poly1305* p, + uint8_t const* data, + unsigned int length) +{ + if (length == 0) + return; + p->prod = p->acc + slowcrypt_poly1305_from_le(data, length, 0x01); + p->prod *= p->r; + p->prod %= ((unsigned _BitInt(136))1 << 130) - (unsigned _BitInt(136))5; + p->acc = p->prod; +} + +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_finish(slowcrypt_poly1305* p, + uint8_t out[16]) +{ + unsigned int i; + p->acc += p->s; + for (i = 0; i < 16; i++) + out[i] = (uint8_t)((p->acc >> (i * 8))); + + for (i = 0; i < sizeof(slowcrypt_poly1305); i++) + ((char*)p)[i] = 0; +} + +#else +static void slowcrypt_poly1305_from_le(slowcrypt_poly1305_bigint_bigint* out, + slowcrypt_poly1305_bigint_bigint* temp, + uint8_t const* buf, + unsigned int buf_len, + uint8_t top) +{ + slowcrypt_poly1305_bigint_from_u8(temp, top); + for (; buf_len-- > 0; buf++) { + slowcrypt_poly1305_bigint_from_u8(temp, *buf); + slowcrypt_poly1305_bigint_lshift_overwrite(out, 8); + slowcrypt_poly1305_bigint_inc(out, temp); + } +} + +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_init(slowcrypt_poly1305* p, + uint8_t key[32]) +{ + (void)slowcrypt_poly1305_bigint_from_cstring( + &p->p, "3fffffffffffffffffffffffffffffffb", 16); + key[3] &= 15; + key[7] &= 15; + key[11] &= 15; + key[15] &= 15; + key[4] &= 252; + key[8] &= 252; + key[12] &= 252; + slowcrypt_poly1305_from_le(&p->r, &p->temp1, key, 16, 0); + slowcrypt_poly1305_from_le(&p->s, &p->temp1, &key[16], 16, 0); +} + +/* only the last block is allowed to be shorter than 16 bytes! */ +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_next_block( + slowcrypt_poly1305* p, + uint8_t const* data, + unsigned int length) +{ + if (length == 0) + return; + slowcrypt_poly1305_from_le(&p->temp1, &p->temp2, data, length, 0x01); + slowcrypt_poly1305_bigint_inc(&p->acc, &p->temp1); + slowcrypt_poly1305_bigint_mul(&p->acc, &p->acc, &p->r); + slowcrypt_poly1305_bigint_div_mod(&p->temp1, &p->acc, &p->acc, &p->p); +} + +SLOWCRYPT_POLY1305_FUNC void slowcrypt_poly1305_finish(slowcrypt_poly1305* p, + uint8_t out[16]) +{ + unsigned int i; + slowcrypt_poly1305_bigint_inc(&p->acc, &p->s); + for (i = 0; i < 16; i++) + out[i] = p->acc.words[i]; + for (i = 0; i < sizeof(slowcrypt_poly1305); i++) + ((char*)p)[i] = 0; +} +#endif + +#endif + +#endif diff --git a/slowcrypt/slowcrypt.c b/slowcrypt/slowcrypt.c new file mode 100644 index 0000000..6b47b25 --- /dev/null +++ b/slowcrypt/slowcrypt.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include + +#define SLOWCRYPT_CHACHA20_IMPL +#include "../chacha20.h" + +#define SLOWCRYPT_POLY1305_IMPL +#include "../poly1305.h" + +struct algo +{ + char const* name; + void (*run)(char**); +}; + +static FILE* file_open(char const* path) +{ + FILE* fp; + + if (!strcmp(path, "-")) + return stdin; + + fp = fopen(path, "rb"); + if (!fp) { + fprintf(stderr, "Could not open %s\n", path); + exit(1); + } + return fp; +} + +static void file_close(FILE* p) +{ + if (!p) + return; + if (p == stdout || p == stdin || p == stderr) + return; + fclose(p); +} + +static int anyeq__impl(char const* str, char const** opts) +{ + for (; *opts; opts++) + if (!strcmp(str, *opts)) + return 1; + return 0; +} +#define anyeq(str, ...) anyeq__impl(str, (char const*[]){__VA_ARGS__, 0}) + +static char const* parse_hex_prefix(char const* msg) +{ + if (*msg == 'h') + msg++; + else if (msg[0] == '0' && msg[1] == 'x') + msg++; + + return msg; +} + +static uint8_t parse_hex_nibble(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 0xA; + if (c >= 'A' && c <= 'F') + return c - 'A' + 0xA; + fprintf(stderr, "Not a hexadecimal number!\n"); + exit(1); +} + +static uint8_t parse_hex(char const** msg) +{ + uint8_t res = parse_hex_nibble(*(*msg)++); + if (**msg) { + res <<= 4; + res |= parse_hex_nibble(*(*msg)++); + } + return res; +} + +static void parse_hex2buf(uint8_t* buf, + unsigned int buflen, + char const* label, + char const* hex) +{ + unsigned int num = 0; + hex = parse_hex_prefix(hex); + for (; num < buflen && *hex; num++) + buf[num] = parse_hex(&hex); + if (num != buflen || *hex) { + fprintf(stderr, "Expected %s to be %u (hexadecimal) bytes!\n", label, + buflen); + exit(1); + } +} + +static unsigned long file_read_chunk(FILE* file, + uint8_t* buf, + unsigned long buflen) +{ + unsigned long n; + if (feof(file)) + return 0; + n = fread(buf, 1, buflen, file); + if (ferror(file)) { + fprintf(stderr, "File read error!"); + exit(1); + } + return n; +} + +static void run_poly1305(char** args) +{ + static char const help[] = + "poly1305 [--key] [file]\n" + "\n" + "Run the Poly1305 one-time authenticator on the data from the given file " + "or stdin\n"; + char const* key = 0; + char const* fpath = "-"; + FILE* fp; + unsigned int npos = 0; + uint8_t keybuf[32]; + uint8_t chunk[16]; + slowcrypt_poly1305 poly1305; + unsigned int nb; + + if (!*args) { + printf("%s", help); + exit(0); + } + + for (; *args; args++) { + if (!key && anyeq(*args, "-k", "-key", "--key") && args[1]) { + args++; + key = *args; + } else if (anyeq(*args, "-h", "-help", "--help")) { + printf("%s", help); + exit(0); + } else if (npos == 1 && ++npos) { + fpath = *args; + } else if (npos == 0 && ++npos && !key) { + key = *args; + } else { + fprintf(stderr, "Unexpected argument: %s\n", *args); + exit(1); + } + } + + if (!key) { + fprintf(stderr, "Missing argument: [--key] "); + exit(1); + } + + fp = file_open(fpath); + + parse_hex2buf(keybuf, 32, "key", key); + slowcrypt_poly1305_init(&poly1305, keybuf); + + while ((nb = file_read_chunk(fp, chunk, 16))) { + slowcrypt_poly1305_next_block(&poly1305, chunk, nb); + } + slowcrypt_poly1305_finish(&poly1305, chunk); + + for (nb = 0; nb < 16; nb++) + printf("%02x", chunk[nb]); + printf("\n"); + + file_close(fp); +} + +static struct algo bytes2sum[] = {{"poly1305", run_poly1305}, {0, 0}}; + +int main(int argc, char** argv) +{ + argv++; + struct algo* a; + + if (!*argv || anyeq(*argv, "-h", "-help", "--help")) { + printf("bytes -> hash\n"); + for (a = bytes2sum; a->name; a++) { + printf(" %s\n", a->name); + } + return 0; + } + + for (a = bytes2sum; a->name; a++) { + if (!strcmp(a->name, *argv)) { + a->run(argv + 1); + return 0; + } + } + + fprintf(stderr, "Unknown algorithm %s\n", *argv); + return 1; +}