mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-13 10:15:00 +00:00
1358 lines
38 KiB
C
1358 lines
38 KiB
C
// portable8439 v1.0.1
|
|
// Source: https://github.com/DavyLandman/portable8439
|
|
// Licensed under CC0-1.0
|
|
// Contains poly1305-donna e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781 (Public
|
|
// Domain)
|
|
|
|
#include "tls_chacha20.h"
|
|
#include "tls.h"
|
|
|
|
#if MG_TLS == MG_TLS_BUILTIN
|
|
// ******* BEGIN: chacha-portable/chacha-portable.h ********
|
|
|
|
#if !defined(__cplusplus) && !defined(_MSC_VER) && \
|
|
(!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
|
|
#error "C99 or newer required"
|
|
#endif
|
|
|
|
#define CHACHA20_KEY_SIZE (32)
|
|
#define CHACHA20_NONCE_SIZE (12)
|
|
|
|
#if defined(_MSC_VER) || defined(__cplusplus)
|
|
// add restrict support
|
|
#if ((defined(_MSC_VER) && _MSC_VER >= 1900) && !defined(__cplusplus)) || \
|
|
defined(__clang__) || defined(__GNUC__)
|
|
#define restrict __restrict
|
|
#else
|
|
#define restrict
|
|
#endif
|
|
#endif
|
|
|
|
// xor data with a ChaCha20 keystream as per RFC8439
|
|
static PORTABLE_8439_DECL void chacha20_xor_stream(
|
|
uint8_t *restrict dest, const uint8_t *restrict source, size_t length,
|
|
const uint8_t key[CHACHA20_KEY_SIZE],
|
|
const uint8_t nonce[CHACHA20_NONCE_SIZE], uint32_t counter);
|
|
|
|
static PORTABLE_8439_DECL void rfc8439_keygen(
|
|
uint8_t poly_key[32], const uint8_t key[CHACHA20_KEY_SIZE],
|
|
const uint8_t nonce[CHACHA20_NONCE_SIZE]);
|
|
|
|
// ******* END: chacha-portable/chacha-portable.h ********
|
|
// ******* BEGIN: poly1305-donna/poly1305-donna.h ********
|
|
|
|
#include <stddef.h>
|
|
|
|
typedef struct poly1305_context {
|
|
size_t aligner;
|
|
unsigned char opaque[136];
|
|
} poly1305_context;
|
|
|
|
static PORTABLE_8439_DECL void poly1305_init(poly1305_context *ctx,
|
|
const unsigned char key[32]);
|
|
static PORTABLE_8439_DECL void poly1305_update(poly1305_context *ctx,
|
|
const unsigned char *m,
|
|
size_t bytes);
|
|
static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
|
|
unsigned char mac[16]);
|
|
|
|
// ******* END: poly1305-donna/poly1305-donna.h ********
|
|
// ******* BEGIN: chacha-portable.c ********
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
// this is a fresh implementation of chacha20, based on the description in
|
|
// rfc8349 it's such a nice compact algorithm that it is easy to do. In
|
|
// relationship to other c implementation this implementation:
|
|
// - pure c99
|
|
// - big & little endian support
|
|
// - safe for architectures that don't support unaligned reads
|
|
//
|
|
// Next to this, we try to be fast as possible without resorting inline
|
|
// assembly.
|
|
|
|
// based on https://sourceforge.net/p/predef/wiki/Endianness/
|
|
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
|
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
#define __HAVE_LITTLE_ENDIAN 1
|
|
#elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \
|
|
defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \
|
|
defined(__MIPSEL) || defined(__MIPSEL__) || defined(__XTENSA_EL__) || \
|
|
defined(__AVR__)
|
|
#define __HAVE_LITTLE_ENDIAN 1
|
|
#endif
|
|
// DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including
|
|
// sys/types.h in GCC
|
|
|
|
#ifndef TEST_SLOW_PATH
|
|
#if defined(__HAVE_LITTLE_ENDIAN)
|
|
#define FAST_PATH
|
|
#endif
|
|
#endif
|
|
|
|
#define CHACHA20_STATE_WORDS (16)
|
|
#define CHACHA20_BLOCK_SIZE (CHACHA20_STATE_WORDS * sizeof(uint32_t))
|
|
|
|
#ifdef FAST_PATH
|
|
#define store_32_le(target, source) memcpy(&(target), source, sizeof(uint32_t))
|
|
#else
|
|
#define store_32_le(target, source) \
|
|
target = (uint32_t) (source)[0] | ((uint32_t) (source)[1]) << 8 | \
|
|
((uint32_t) (source)[2]) << 16 | ((uint32_t) (source)[3]) << 24
|
|
#endif
|
|
|
|
static void initialize_state(uint32_t state[CHACHA20_STATE_WORDS],
|
|
const uint8_t key[CHACHA20_KEY_SIZE],
|
|
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
|
uint32_t counter) {
|
|
#if 0
|
|
#ifdef static_assert
|
|
static_assert(sizeof(uint32_t) == 4,
|
|
"We don't support systems that do not conform to standard of "
|
|
"uint32_t being exact 32bit wide");
|
|
#endif
|
|
#endif
|
|
state[0] = 0x61707865;
|
|
state[1] = 0x3320646e;
|
|
state[2] = 0x79622d32;
|
|
state[3] = 0x6b206574;
|
|
store_32_le(state[4], key);
|
|
store_32_le(state[5], key + 4);
|
|
store_32_le(state[6], key + 8);
|
|
store_32_le(state[7], key + 12);
|
|
store_32_le(state[8], key + 16);
|
|
store_32_le(state[9], key + 20);
|
|
store_32_le(state[10], key + 24);
|
|
store_32_le(state[11], key + 28);
|
|
state[12] = counter;
|
|
store_32_le(state[13], nonce);
|
|
store_32_le(state[14], nonce + 4);
|
|
store_32_le(state[15], nonce + 8);
|
|
}
|
|
|
|
#define increment_counter(state) (state)[12]++
|
|
|
|
// source: http://blog.regehr.org/archives/1063
|
|
#define rotl32a(x, n) ((x) << (n)) | ((x) >> (32 - (n)))
|
|
|
|
#define Qround(a, b, c, d) \
|
|
a += b; \
|
|
d ^= a; \
|
|
d = rotl32a(d, 16); \
|
|
c += d; \
|
|
b ^= c; \
|
|
b = rotl32a(b, 12); \
|
|
a += b; \
|
|
d ^= a; \
|
|
d = rotl32a(d, 8); \
|
|
c += d; \
|
|
b ^= c; \
|
|
b = rotl32a(b, 7);
|
|
|
|
#define TIMES16(x) \
|
|
x(0) x(1) x(2) x(3) x(4) x(5) x(6) x(7) x(8) x(9) x(10) x(11) x(12) x(13) \
|
|
x(14) x(15)
|
|
|
|
static void core_block(const uint32_t *restrict start,
|
|
uint32_t *restrict output) {
|
|
int i;
|
|
// instead of working on the output array,
|
|
// we let the compiler allocate 16 local variables on the stack
|
|
#define __LV(i) uint32_t __t##i = start[i];
|
|
TIMES16(__LV)
|
|
|
|
#define __Q(a, b, c, d) Qround(__t##a, __t##b, __t##c, __t##d)
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
__Q(0, 4, 8, 12);
|
|
__Q(1, 5, 9, 13);
|
|
__Q(2, 6, 10, 14);
|
|
__Q(3, 7, 11, 15);
|
|
__Q(0, 5, 10, 15);
|
|
__Q(1, 6, 11, 12);
|
|
__Q(2, 7, 8, 13);
|
|
__Q(3, 4, 9, 14);
|
|
}
|
|
|
|
#define __FIN(i) output[i] = start[i] + __t##i;
|
|
TIMES16(__FIN)
|
|
}
|
|
|
|
#define U8(x) ((uint8_t) ((x) & 0xFF))
|
|
|
|
#ifdef FAST_PATH
|
|
#define xor32_le(dst, src, pad) \
|
|
uint32_t __value; \
|
|
memcpy(&__value, src, sizeof(uint32_t)); \
|
|
__value ^= *(pad); \
|
|
memcpy(dst, &__value, sizeof(uint32_t));
|
|
#else
|
|
#define xor32_le(dst, src, pad) \
|
|
(dst)[0] = (src)[0] ^ U8(*(pad)); \
|
|
(dst)[1] = (src)[1] ^ U8(*(pad) >> 8); \
|
|
(dst)[2] = (src)[2] ^ U8(*(pad) >> 16); \
|
|
(dst)[3] = (src)[3] ^ U8(*(pad) >> 24);
|
|
#endif
|
|
|
|
#define index8_32(a, ix) ((a) + ((ix) * sizeof(uint32_t)))
|
|
|
|
#define xor32_blocks(dest, source, pad, words) \
|
|
for (i = 0; i < words; i++) { \
|
|
xor32_le(index8_32(dest, i), index8_32(source, i), (pad) + i) \
|
|
}
|
|
|
|
static void xor_block(uint8_t *restrict dest, const uint8_t *restrict source,
|
|
const uint32_t *restrict pad, unsigned int chunk_size) {
|
|
unsigned int i, full_blocks = chunk_size / (unsigned int) sizeof(uint32_t);
|
|
// have to be carefull, we are going back from uint32 to uint8, so endianness
|
|
// matters again
|
|
xor32_blocks(dest, source, pad, full_blocks)
|
|
|
|
dest += full_blocks * sizeof(uint32_t);
|
|
source += full_blocks * sizeof(uint32_t);
|
|
pad += full_blocks;
|
|
|
|
switch (chunk_size % sizeof(uint32_t)) {
|
|
case 1:
|
|
dest[0] = source[0] ^ U8(*pad);
|
|
break;
|
|
case 2:
|
|
dest[0] = source[0] ^ U8(*pad);
|
|
dest[1] = source[1] ^ U8(*pad >> 8);
|
|
break;
|
|
case 3:
|
|
dest[0] = source[0] ^ U8(*pad);
|
|
dest[1] = source[1] ^ U8(*pad >> 8);
|
|
dest[2] = source[2] ^ U8(*pad >> 16);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void chacha20_xor_stream(uint8_t *restrict dest,
|
|
const uint8_t *restrict source, size_t length,
|
|
const uint8_t key[CHACHA20_KEY_SIZE],
|
|
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
|
uint32_t counter) {
|
|
uint32_t state[CHACHA20_STATE_WORDS];
|
|
uint32_t pad[CHACHA20_STATE_WORDS];
|
|
size_t i, b, last_block, full_blocks = length / CHACHA20_BLOCK_SIZE;
|
|
initialize_state(state, key, nonce, counter);
|
|
for (b = 0; b < full_blocks; b++) {
|
|
core_block(state, pad);
|
|
increment_counter(state);
|
|
xor32_blocks(dest, source, pad, CHACHA20_STATE_WORDS) dest +=
|
|
CHACHA20_BLOCK_SIZE;
|
|
source += CHACHA20_BLOCK_SIZE;
|
|
}
|
|
last_block = length % CHACHA20_BLOCK_SIZE;
|
|
if (last_block > 0) {
|
|
core_block(state, pad);
|
|
xor_block(dest, source, pad, (unsigned int) last_block);
|
|
}
|
|
}
|
|
|
|
#ifdef FAST_PATH
|
|
#define serialize(poly_key, result) memcpy(poly_key, result, 32)
|
|
#else
|
|
#define store32_le(target, source) \
|
|
(target)[0] = U8(*(source)); \
|
|
(target)[1] = U8(*(source) >> 8); \
|
|
(target)[2] = U8(*(source) >> 16); \
|
|
(target)[3] = U8(*(source) >> 24);
|
|
|
|
#define serialize(poly_key, result) \
|
|
for (i = 0; i < 32 / sizeof(uint32_t); i++) { \
|
|
store32_le(index8_32(poly_key, i), result + i); \
|
|
}
|
|
#endif
|
|
|
|
static void rfc8439_keygen(uint8_t poly_key[32],
|
|
const uint8_t key[CHACHA20_KEY_SIZE],
|
|
const uint8_t nonce[CHACHA20_NONCE_SIZE]) {
|
|
uint32_t state[CHACHA20_STATE_WORDS];
|
|
uint32_t result[CHACHA20_STATE_WORDS];
|
|
size_t i;
|
|
initialize_state(state, key, nonce, 0);
|
|
core_block(state, result);
|
|
serialize(poly_key, result);
|
|
(void) i;
|
|
}
|
|
// ******* END: chacha-portable.c ********
|
|
// ******* BEGIN: poly1305-donna.c ********
|
|
|
|
/* auto detect between 32bit / 64bit */
|
|
#if /* uint128 available on 64bit system*/ \
|
|
(defined(__SIZEOF_INT128__) && \
|
|
defined(__LP64__)) /* MSVC 64bit compiler */ \
|
|
|| (defined(_MSC_VER) && defined(_M_X64)) /* gcc >= 4.4 64bit */ \
|
|
|| (defined(__GNUC__) && defined(__LP64__) && \
|
|
((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4))))
|
|
#define __GUESS64
|
|
#else
|
|
#define __GUESS32
|
|
#endif
|
|
|
|
#if defined(POLY1305_8BIT)
|
|
/*
|
|
poly1305 implementation using 8 bit * 8 bit = 16 bit multiplication and
|
|
32 bit addition
|
|
|
|
based on the public domain reference version in supercop by djb
|
|
static */
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#define POLY1305_NOINLINE
|
|
#elif defined(_MSC_VER)
|
|
#define POLY1305_NOINLINE __declspec(noinline)
|
|
#elif defined(__GNUC__)
|
|
#define POLY1305_NOINLINE __attribute__((noinline))
|
|
#else
|
|
#define POLY1305_NOINLINE
|
|
#endif
|
|
|
|
#define poly1305_block_size 16
|
|
|
|
/* 17 + sizeof(size_t) + 51*sizeof(unsigned char) */
|
|
typedef struct poly1305_state_internal_t {
|
|
unsigned char buffer[poly1305_block_size];
|
|
size_t leftover;
|
|
unsigned char h[17];
|
|
unsigned char r[17];
|
|
unsigned char pad[17];
|
|
unsigned char final;
|
|
} poly1305_state_internal_t;
|
|
|
|
static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
size_t i;
|
|
|
|
st->leftover = 0;
|
|
|
|
/* h = 0 */
|
|
for (i = 0; i < 17; i++) st->h[i] = 0;
|
|
|
|
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
|
st->r[0] = key[0] & 0xff;
|
|
st->r[1] = key[1] & 0xff;
|
|
st->r[2] = key[2] & 0xff;
|
|
st->r[3] = key[3] & 0x0f;
|
|
st->r[4] = key[4] & 0xfc;
|
|
st->r[5] = key[5] & 0xff;
|
|
st->r[6] = key[6] & 0xff;
|
|
st->r[7] = key[7] & 0x0f;
|
|
st->r[8] = key[8] & 0xfc;
|
|
st->r[9] = key[9] & 0xff;
|
|
st->r[10] = key[10] & 0xff;
|
|
st->r[11] = key[11] & 0x0f;
|
|
st->r[12] = key[12] & 0xfc;
|
|
st->r[13] = key[13] & 0xff;
|
|
st->r[14] = key[14] & 0xff;
|
|
st->r[15] = key[15] & 0x0f;
|
|
st->r[16] = 0;
|
|
|
|
/* save pad for later */
|
|
for (i = 0; i < 16; i++) st->pad[i] = key[i + 16];
|
|
st->pad[16] = 0;
|
|
|
|
st->final = 0;
|
|
}
|
|
|
|
static void poly1305_add(unsigned char h[17], const unsigned char c[17]) {
|
|
unsigned short u;
|
|
unsigned int i;
|
|
for (u = 0, i = 0; i < 17; i++) {
|
|
u += (unsigned short) h[i] + (unsigned short) c[i];
|
|
h[i] = (unsigned char) u & 0xff;
|
|
u >>= 8;
|
|
}
|
|
}
|
|
|
|
static void poly1305_squeeze(unsigned char h[17], unsigned long hr[17]) {
|
|
unsigned long u;
|
|
unsigned int i;
|
|
u = 0;
|
|
for (i = 0; i < 16; i++) {
|
|
u += hr[i];
|
|
h[i] = (unsigned char) u & 0xff;
|
|
u >>= 8;
|
|
}
|
|
u += hr[16];
|
|
h[16] = (unsigned char) u & 0x03;
|
|
u >>= 2;
|
|
u += (u << 2); /* u *= 5; */
|
|
for (i = 0; i < 16; i++) {
|
|
u += h[i];
|
|
h[i] = (unsigned char) u & 0xff;
|
|
u >>= 8;
|
|
}
|
|
h[16] += (unsigned char) u;
|
|
}
|
|
|
|
static void poly1305_freeze(unsigned char h[17]) {
|
|
const unsigned char minusp[17] = {0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0xfc};
|
|
unsigned char horig[17], negative;
|
|
unsigned int i;
|
|
|
|
/* compute h + -p */
|
|
for (i = 0; i < 17; i++) horig[i] = h[i];
|
|
poly1305_add(h, minusp);
|
|
|
|
/* select h if h < p, or h + -p if h >= p */
|
|
negative = -(h[16] >> 7);
|
|
for (i = 0; i < 17; i++) h[i] ^= negative & (horig[i] ^ h[i]);
|
|
}
|
|
|
|
static void poly1305_blocks(poly1305_state_internal_t *st,
|
|
const unsigned char *m, size_t bytes) {
|
|
const unsigned char hibit = st->final ^ 1; /* 1 << 128 */
|
|
|
|
while (bytes >= poly1305_block_size) {
|
|
unsigned long hr[17], u;
|
|
unsigned char c[17];
|
|
unsigned int i, j;
|
|
|
|
/* h += m */
|
|
for (i = 0; i < 16; i++) c[i] = m[i];
|
|
c[16] = hibit;
|
|
poly1305_add(st->h, c);
|
|
|
|
/* h *= r */
|
|
for (i = 0; i < 17; i++) {
|
|
u = 0;
|
|
for (j = 0; j <= i; j++) {
|
|
u += (unsigned short) st->h[j] * st->r[i - j];
|
|
}
|
|
for (j = i + 1; j < 17; j++) {
|
|
unsigned long v = (unsigned short) st->h[j] * st->r[i + 17 - j];
|
|
v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
|
|
u += v;
|
|
}
|
|
hr[i] = u;
|
|
}
|
|
|
|
/* (partial) h %= p */
|
|
poly1305_squeeze(st->h, hr);
|
|
|
|
m += poly1305_block_size;
|
|
bytes -= poly1305_block_size;
|
|
}
|
|
}
|
|
|
|
static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
|
|
unsigned char mac[16]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
size_t i;
|
|
|
|
/* process the remaining block */
|
|
if (st->leftover) {
|
|
size_t i = st->leftover;
|
|
st->buffer[i++] = 1;
|
|
for (; i < poly1305_block_size; i++) st->buffer[i] = 0;
|
|
st->final = 1;
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
}
|
|
|
|
/* fully reduce h */
|
|
poly1305_freeze(st->h);
|
|
|
|
/* h = (h + pad) % (1 << 128) */
|
|
poly1305_add(st->h, st->pad);
|
|
for (i = 0; i < 16; i++) mac[i] = st->h[i];
|
|
|
|
/* zero out the state */
|
|
for (i = 0; i < 17; i++) st->h[i] = 0;
|
|
for (i = 0; i < 17; i++) st->r[i] = 0;
|
|
for (i = 0; i < 17; i++) st->pad[i] = 0;
|
|
}
|
|
#elif defined(POLY1305_16BIT)
|
|
/*
|
|
poly1305 implementation using 16 bit * 16 bit = 32 bit multiplication
|
|
and 32 bit addition static */
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#define POLY1305_NOINLINE
|
|
#elif defined(_MSC_VER)
|
|
#define POLY1305_NOINLINE __declspec(noinline)
|
|
#elif defined(__GNUC__)
|
|
#define POLY1305_NOINLINE __attribute__((noinline))
|
|
#else
|
|
#define POLY1305_NOINLINE
|
|
#endif
|
|
|
|
#define poly1305_block_size 16
|
|
|
|
/* 17 + sizeof(size_t) + 18*sizeof(unsigned short) */
|
|
typedef struct poly1305_state_internal_t {
|
|
unsigned char buffer[poly1305_block_size];
|
|
size_t leftover;
|
|
unsigned short r[10];
|
|
unsigned short h[10];
|
|
unsigned short pad[8];
|
|
unsigned char final;
|
|
} poly1305_state_internal_t;
|
|
|
|
/* interpret two 8 bit unsigned integers as a 16 bit unsigned integer in little
|
|
* endian */
|
|
static unsigned short U8TO16(const unsigned char *p) {
|
|
return (((unsigned short) (p[0] & 0xff)) |
|
|
((unsigned short) (p[1] & 0xff) << 8));
|
|
}
|
|
|
|
/* store a 16 bit unsigned integer as two 8 bit unsigned integers in little
|
|
* endian */
|
|
static void U16TO8(unsigned char *p, unsigned short v) {
|
|
p[0] = (v) & 0xff;
|
|
p[1] = (v >> 8) & 0xff;
|
|
}
|
|
|
|
static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
unsigned short t0, t1, t2, t3, t4, t5, t6, t7;
|
|
size_t i;
|
|
|
|
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
|
t0 = U8TO16(&key[0]);
|
|
st->r[0] = (t0) & 0x1fff;
|
|
t1 = U8TO16(&key[2]);
|
|
st->r[1] = ((t0 >> 13) | (t1 << 3)) & 0x1fff;
|
|
t2 = U8TO16(&key[4]);
|
|
st->r[2] = ((t1 >> 10) | (t2 << 6)) & 0x1f03;
|
|
t3 = U8TO16(&key[6]);
|
|
st->r[3] = ((t2 >> 7) | (t3 << 9)) & 0x1fff;
|
|
t4 = U8TO16(&key[8]);
|
|
st->r[4] = ((t3 >> 4) | (t4 << 12)) & 0x00ff;
|
|
st->r[5] = ((t4 >> 1)) & 0x1ffe;
|
|
t5 = U8TO16(&key[10]);
|
|
st->r[6] = ((t4 >> 14) | (t5 << 2)) & 0x1fff;
|
|
t6 = U8TO16(&key[12]);
|
|
st->r[7] = ((t5 >> 11) | (t6 << 5)) & 0x1f81;
|
|
t7 = U8TO16(&key[14]);
|
|
st->r[8] = ((t6 >> 8) | (t7 << 8)) & 0x1fff;
|
|
st->r[9] = ((t7 >> 5)) & 0x007f;
|
|
|
|
/* h = 0 */
|
|
for (i = 0; i < 10; i++) st->h[i] = 0;
|
|
|
|
/* save pad for later */
|
|
for (i = 0; i < 8; i++) st->pad[i] = U8TO16(&key[16 + (2 * i)]);
|
|
|
|
st->leftover = 0;
|
|
st->final = 0;
|
|
}
|
|
|
|
static void poly1305_blocks(poly1305_state_internal_t *st,
|
|
const unsigned char *m, size_t bytes) {
|
|
const unsigned short hibit = (st->final) ? 0 : (1 << 11); /* 1 << 128 */
|
|
unsigned short t0, t1, t2, t3, t4, t5, t6, t7;
|
|
unsigned long d[10];
|
|
unsigned long c;
|
|
|
|
while (bytes >= poly1305_block_size) {
|
|
size_t i, j;
|
|
|
|
/* h += m[i] */
|
|
t0 = U8TO16(&m[0]);
|
|
st->h[0] += (t0) & 0x1fff;
|
|
t1 = U8TO16(&m[2]);
|
|
st->h[1] += ((t0 >> 13) | (t1 << 3)) & 0x1fff;
|
|
t2 = U8TO16(&m[4]);
|
|
st->h[2] += ((t1 >> 10) | (t2 << 6)) & 0x1fff;
|
|
t3 = U8TO16(&m[6]);
|
|
st->h[3] += ((t2 >> 7) | (t3 << 9)) & 0x1fff;
|
|
t4 = U8TO16(&m[8]);
|
|
st->h[4] += ((t3 >> 4) | (t4 << 12)) & 0x1fff;
|
|
st->h[5] += ((t4 >> 1)) & 0x1fff;
|
|
t5 = U8TO16(&m[10]);
|
|
st->h[6] += ((t4 >> 14) | (t5 << 2)) & 0x1fff;
|
|
t6 = U8TO16(&m[12]);
|
|
st->h[7] += ((t5 >> 11) | (t6 << 5)) & 0x1fff;
|
|
t7 = U8TO16(&m[14]);
|
|
st->h[8] += ((t6 >> 8) | (t7 << 8)) & 0x1fff;
|
|
st->h[9] += ((t7 >> 5)) | hibit;
|
|
|
|
/* h *= r, (partial) h %= p */
|
|
for (i = 0, c = 0; i < 10; i++) {
|
|
d[i] = c;
|
|
for (j = 0; j < 10; j++) {
|
|
d[i] += (unsigned long) st->h[j] *
|
|
((j <= i) ? st->r[i - j] : (5 * st->r[i + 10 - j]));
|
|
/* Sum(h[i] * r[i] * 5) will overflow slightly above 6 products with an
|
|
* unclamped r, so carry at 5 */
|
|
if (j == 4) {
|
|
c = (d[i] >> 13);
|
|
d[i] &= 0x1fff;
|
|
}
|
|
}
|
|
c += (d[i] >> 13);
|
|
d[i] &= 0x1fff;
|
|
}
|
|
c = ((c << 2) + c); /* c *= 5 */
|
|
c += d[0];
|
|
d[0] = ((unsigned short) c & 0x1fff);
|
|
c = (c >> 13);
|
|
d[1] += c;
|
|
|
|
for (i = 0; i < 10; i++) st->h[i] = (unsigned short) d[i];
|
|
|
|
m += poly1305_block_size;
|
|
bytes -= poly1305_block_size;
|
|
}
|
|
}
|
|
|
|
static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
|
|
unsigned char mac[16]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
unsigned short c;
|
|
unsigned short g[10];
|
|
unsigned short mask;
|
|
unsigned long f;
|
|
size_t i;
|
|
|
|
/* process the remaining block */
|
|
if (st->leftover) {
|
|
size_t i = st->leftover;
|
|
st->buffer[i++] = 1;
|
|
for (; i < poly1305_block_size; i++) st->buffer[i] = 0;
|
|
st->final = 1;
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
}
|
|
|
|
/* fully carry h */
|
|
c = st->h[1] >> 13;
|
|
st->h[1] &= 0x1fff;
|
|
for (i = 2; i < 10; i++) {
|
|
st->h[i] += c;
|
|
c = st->h[i] >> 13;
|
|
st->h[i] &= 0x1fff;
|
|
}
|
|
st->h[0] += (c * 5);
|
|
c = st->h[0] >> 13;
|
|
st->h[0] &= 0x1fff;
|
|
st->h[1] += c;
|
|
c = st->h[1] >> 13;
|
|
st->h[1] &= 0x1fff;
|
|
st->h[2] += c;
|
|
|
|
/* compute h + -p */
|
|
g[0] = st->h[0] + 5;
|
|
c = g[0] >> 13;
|
|
g[0] &= 0x1fff;
|
|
for (i = 1; i < 10; i++) {
|
|
g[i] = st->h[i] + c;
|
|
c = g[i] >> 13;
|
|
g[i] &= 0x1fff;
|
|
}
|
|
|
|
/* select h if h < p, or h + -p if h >= p */
|
|
mask = (c ^ 1) - 1;
|
|
for (i = 0; i < 10; i++) g[i] &= mask;
|
|
mask = ~mask;
|
|
for (i = 0; i < 10; i++) st->h[i] = (st->h[i] & mask) | g[i];
|
|
|
|
/* h = h % (2^128) */
|
|
st->h[0] = ((st->h[0]) | (st->h[1] << 13)) & 0xffff;
|
|
st->h[1] = ((st->h[1] >> 3) | (st->h[2] << 10)) & 0xffff;
|
|
st->h[2] = ((st->h[2] >> 6) | (st->h[3] << 7)) & 0xffff;
|
|
st->h[3] = ((st->h[3] >> 9) | (st->h[4] << 4)) & 0xffff;
|
|
st->h[4] = ((st->h[4] >> 12) | (st->h[5] << 1) | (st->h[6] << 14)) & 0xffff;
|
|
st->h[5] = ((st->h[6] >> 2) | (st->h[7] << 11)) & 0xffff;
|
|
st->h[6] = ((st->h[7] >> 5) | (st->h[8] << 8)) & 0xffff;
|
|
st->h[7] = ((st->h[8] >> 8) | (st->h[9] << 5)) & 0xffff;
|
|
|
|
/* mac = (h + pad) % (2^128) */
|
|
f = (unsigned long) st->h[0] + st->pad[0];
|
|
st->h[0] = (unsigned short) f;
|
|
for (i = 1; i < 8; i++) {
|
|
f = (unsigned long) st->h[i] + st->pad[i] + (f >> 16);
|
|
st->h[i] = (unsigned short) f;
|
|
}
|
|
|
|
for (i = 0; i < 8; i++) U16TO8(mac + (i * 2), st->h[i]);
|
|
|
|
/* zero out the state */
|
|
for (i = 0; i < 10; i++) st->h[i] = 0;
|
|
for (i = 0; i < 10; i++) st->r[i] = 0;
|
|
for (i = 0; i < 8; i++) st->pad[i] = 0;
|
|
}
|
|
#elif defined(POLY1305_32BIT) || \
|
|
(!defined(POLY1305_64BIT) && defined(__GUESS32))
|
|
/*
|
|
poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication
|
|
and 64 bit addition static */
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#define POLY1305_NOINLINE
|
|
#elif defined(_MSC_VER)
|
|
#define POLY1305_NOINLINE __declspec(noinline)
|
|
#elif defined(__GNUC__)
|
|
#define POLY1305_NOINLINE __attribute__((noinline))
|
|
#else
|
|
#define POLY1305_NOINLINE
|
|
#endif
|
|
|
|
#define poly1305_block_size 16
|
|
|
|
/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
|
|
typedef struct poly1305_state_internal_t {
|
|
unsigned long r[5];
|
|
unsigned long h[5];
|
|
unsigned long pad[4];
|
|
size_t leftover;
|
|
unsigned char buffer[poly1305_block_size];
|
|
unsigned char final;
|
|
} poly1305_state_internal_t;
|
|
|
|
/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little
|
|
* endian */
|
|
static unsigned long U8TO32(const unsigned char *p) {
|
|
return (((unsigned long) (p[0] & 0xff)) |
|
|
((unsigned long) (p[1] & 0xff) << 8) |
|
|
((unsigned long) (p[2] & 0xff) << 16) |
|
|
((unsigned long) (p[3] & 0xff) << 24));
|
|
}
|
|
|
|
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little
|
|
* endian */
|
|
static void U32TO8(unsigned char *p, unsigned long v) {
|
|
p[0] = (unsigned char) ((v) & 0xff);
|
|
p[1] = (unsigned char) ((v >> 8) & 0xff);
|
|
p[2] = (unsigned char) ((v >> 16) & 0xff);
|
|
p[3] = (unsigned char) ((v >> 24) & 0xff);
|
|
}
|
|
|
|
static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
|
|
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
|
st->r[0] = (U8TO32(&key[0])) & 0x3ffffff;
|
|
st->r[1] = (U8TO32(&key[3]) >> 2) & 0x3ffff03;
|
|
st->r[2] = (U8TO32(&key[6]) >> 4) & 0x3ffc0ff;
|
|
st->r[3] = (U8TO32(&key[9]) >> 6) & 0x3f03fff;
|
|
st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
|
|
|
|
/* h = 0 */
|
|
st->h[0] = 0;
|
|
st->h[1] = 0;
|
|
st->h[2] = 0;
|
|
st->h[3] = 0;
|
|
st->h[4] = 0;
|
|
|
|
/* save pad for later */
|
|
st->pad[0] = U8TO32(&key[16]);
|
|
st->pad[1] = U8TO32(&key[20]);
|
|
st->pad[2] = U8TO32(&key[24]);
|
|
st->pad[3] = U8TO32(&key[28]);
|
|
|
|
st->leftover = 0;
|
|
st->final = 0;
|
|
}
|
|
|
|
static void poly1305_blocks(poly1305_state_internal_t *st,
|
|
const unsigned char *m, size_t bytes) {
|
|
const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */
|
|
unsigned long r0, r1, r2, r3, r4;
|
|
unsigned long s1, s2, s3, s4;
|
|
unsigned long h0, h1, h2, h3, h4;
|
|
uint64_t d0, d1, d2, d3, d4;
|
|
unsigned long c;
|
|
|
|
r0 = st->r[0];
|
|
r1 = st->r[1];
|
|
r2 = st->r[2];
|
|
r3 = st->r[3];
|
|
r4 = st->r[4];
|
|
|
|
s1 = r1 * 5;
|
|
s2 = r2 * 5;
|
|
s3 = r3 * 5;
|
|
s4 = r4 * 5;
|
|
|
|
h0 = st->h[0];
|
|
h1 = st->h[1];
|
|
h2 = st->h[2];
|
|
h3 = st->h[3];
|
|
h4 = st->h[4];
|
|
|
|
while (bytes >= poly1305_block_size) {
|
|
/* h += m[i] */
|
|
h0 += (U8TO32(m + 0)) & 0x3ffffff;
|
|
h1 += (U8TO32(m + 3) >> 2) & 0x3ffffff;
|
|
h2 += (U8TO32(m + 6) >> 4) & 0x3ffffff;
|
|
h3 += (U8TO32(m + 9) >> 6) & 0x3ffffff;
|
|
h4 += (U8TO32(m + 12) >> 8) | hibit;
|
|
|
|
/* h *= r */
|
|
d0 = ((uint64_t) h0 * r0) + ((uint64_t) h1 * s4) + ((uint64_t) h2 * s3) +
|
|
((uint64_t) h3 * s2) + ((uint64_t) h4 * s1);
|
|
d1 = ((uint64_t) h0 * r1) + ((uint64_t) h1 * r0) + ((uint64_t) h2 * s4) +
|
|
((uint64_t) h3 * s3) + ((uint64_t) h4 * s2);
|
|
d2 = ((uint64_t) h0 * r2) + ((uint64_t) h1 * r1) + ((uint64_t) h2 * r0) +
|
|
((uint64_t) h3 * s4) + ((uint64_t) h4 * s3);
|
|
d3 = ((uint64_t) h0 * r3) + ((uint64_t) h1 * r2) + ((uint64_t) h2 * r1) +
|
|
((uint64_t) h3 * r0) + ((uint64_t) h4 * s4);
|
|
d4 = ((uint64_t) h0 * r4) + ((uint64_t) h1 * r3) + ((uint64_t) h2 * r2) +
|
|
((uint64_t) h3 * r1) + ((uint64_t) h4 * r0);
|
|
|
|
/* (partial) h %= p */
|
|
c = (unsigned long) (d0 >> 26);
|
|
h0 = (unsigned long) d0 & 0x3ffffff;
|
|
d1 += c;
|
|
c = (unsigned long) (d1 >> 26);
|
|
h1 = (unsigned long) d1 & 0x3ffffff;
|
|
d2 += c;
|
|
c = (unsigned long) (d2 >> 26);
|
|
h2 = (unsigned long) d2 & 0x3ffffff;
|
|
d3 += c;
|
|
c = (unsigned long) (d3 >> 26);
|
|
h3 = (unsigned long) d3 & 0x3ffffff;
|
|
d4 += c;
|
|
c = (unsigned long) (d4 >> 26);
|
|
h4 = (unsigned long) d4 & 0x3ffffff;
|
|
h0 += c * 5;
|
|
c = (h0 >> 26);
|
|
h0 = h0 & 0x3ffffff;
|
|
h1 += c;
|
|
|
|
m += poly1305_block_size;
|
|
bytes -= poly1305_block_size;
|
|
}
|
|
|
|
st->h[0] = h0;
|
|
st->h[1] = h1;
|
|
st->h[2] = h2;
|
|
st->h[3] = h3;
|
|
st->h[4] = h4;
|
|
}
|
|
|
|
static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
|
|
unsigned char mac[16]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
unsigned long h0, h1, h2, h3, h4, c;
|
|
unsigned long g0, g1, g2, g3, g4;
|
|
uint64_t f;
|
|
unsigned long mask;
|
|
|
|
/* process the remaining block */
|
|
if (st->leftover) {
|
|
size_t i = st->leftover;
|
|
st->buffer[i++] = 1;
|
|
for (; i < poly1305_block_size; i++) st->buffer[i] = 0;
|
|
st->final = 1;
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
}
|
|
|
|
/* fully carry h */
|
|
h0 = st->h[0];
|
|
h1 = st->h[1];
|
|
h2 = st->h[2];
|
|
h3 = st->h[3];
|
|
h4 = st->h[4];
|
|
|
|
c = h1 >> 26;
|
|
h1 = h1 & 0x3ffffff;
|
|
h2 += c;
|
|
c = h2 >> 26;
|
|
h2 = h2 & 0x3ffffff;
|
|
h3 += c;
|
|
c = h3 >> 26;
|
|
h3 = h3 & 0x3ffffff;
|
|
h4 += c;
|
|
c = h4 >> 26;
|
|
h4 = h4 & 0x3ffffff;
|
|
h0 += c * 5;
|
|
c = h0 >> 26;
|
|
h0 = h0 & 0x3ffffff;
|
|
h1 += c;
|
|
|
|
/* compute h + -p */
|
|
g0 = h0 + 5;
|
|
c = g0 >> 26;
|
|
g0 &= 0x3ffffff;
|
|
g1 = h1 + c;
|
|
c = g1 >> 26;
|
|
g1 &= 0x3ffffff;
|
|
g2 = h2 + c;
|
|
c = g2 >> 26;
|
|
g2 &= 0x3ffffff;
|
|
g3 = h3 + c;
|
|
c = g3 >> 26;
|
|
g3 &= 0x3ffffff;
|
|
g4 = h4 + c - (1UL << 26);
|
|
|
|
/* select h if h < p, or h + -p if h >= p */
|
|
mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
|
|
g0 &= mask;
|
|
g1 &= mask;
|
|
g2 &= mask;
|
|
g3 &= mask;
|
|
g4 &= mask;
|
|
mask = ~mask;
|
|
h0 = (h0 & mask) | g0;
|
|
h1 = (h1 & mask) | g1;
|
|
h2 = (h2 & mask) | g2;
|
|
h3 = (h3 & mask) | g3;
|
|
h4 = (h4 & mask) | g4;
|
|
|
|
/* h = h % (2^128) */
|
|
h0 = ((h0) | (h1 << 26)) & 0xffffffff;
|
|
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
|
|
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
|
|
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
|
|
|
|
/* mac = (h + pad) % (2^128) */
|
|
f = (uint64_t) h0 + st->pad[0];
|
|
h0 = (unsigned long) f;
|
|
f = (uint64_t) h1 + st->pad[1] + (f >> 32);
|
|
h1 = (unsigned long) f;
|
|
f = (uint64_t) h2 + st->pad[2] + (f >> 32);
|
|
h2 = (unsigned long) f;
|
|
f = (uint64_t) h3 + st->pad[3] + (f >> 32);
|
|
h3 = (unsigned long) f;
|
|
|
|
U32TO8(mac + 0, h0);
|
|
U32TO8(mac + 4, h1);
|
|
U32TO8(mac + 8, h2);
|
|
U32TO8(mac + 12, h3);
|
|
|
|
/* zero out the state */
|
|
st->h[0] = 0;
|
|
st->h[1] = 0;
|
|
st->h[2] = 0;
|
|
st->h[3] = 0;
|
|
st->h[4] = 0;
|
|
st->r[0] = 0;
|
|
st->r[1] = 0;
|
|
st->r[2] = 0;
|
|
st->r[3] = 0;
|
|
st->r[4] = 0;
|
|
st->pad[0] = 0;
|
|
st->pad[1] = 0;
|
|
st->pad[2] = 0;
|
|
st->pad[3] = 0;
|
|
}
|
|
|
|
#else
|
|
/*
|
|
poly1305 implementation using 64 bit * 64 bit = 128 bit multiplication
|
|
and 128 bit addition static */
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
typedef struct uint128_t {
|
|
uint64_t lo;
|
|
uint64_t hi;
|
|
} uint128_t;
|
|
|
|
#define MUL128(out, x, y) out.lo = _umul128((x), (y), &out.hi)
|
|
#define ADD(out, in) \
|
|
{ \
|
|
uint64_t t = out.lo; \
|
|
out.lo += in.lo; \
|
|
out.hi += (out.lo < t) + in.hi; \
|
|
}
|
|
#define ADDLO(out, in) \
|
|
{ \
|
|
uint64_t t = out.lo; \
|
|
out.lo += in; \
|
|
out.hi += (out.lo < t); \
|
|
}
|
|
#define SHR(in, shift) (__shiftright128(in.lo, in.hi, (shift)))
|
|
#define LO(in) (in.lo)
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#define POLY1305_NOINLINE
|
|
#else
|
|
#define POLY1305_NOINLINE __declspec(noinline)
|
|
#endif
|
|
#elif defined(__GNUC__)
|
|
#if defined(__SIZEOF_INT128__)
|
|
// Get rid of GCC warning "ISO C does not support '__int128' types"
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
typedef unsigned __int128 uint128_t;
|
|
#pragma GCC diagnostic pop
|
|
#else
|
|
typedef unsigned uint128_t __attribute__((mode(TI)));
|
|
#endif
|
|
|
|
#define MUL128(out, x, y) out = ((uint128_t) x * y)
|
|
#define ADD(out, in) out += in
|
|
#define ADDLO(out, in) out += in
|
|
#define SHR(in, shift) (uint64_t) (in >> (shift))
|
|
#define LO(in) (uint64_t) (in)
|
|
|
|
#define POLY1305_NOINLINE __attribute__((noinline))
|
|
#endif
|
|
|
|
#define poly1305_block_size 16
|
|
|
|
/* 17 + sizeof(size_t) + 8*sizeof(uint64_t) */
|
|
typedef struct poly1305_state_internal_t {
|
|
uint64_t r[3];
|
|
uint64_t h[3];
|
|
uint64_t pad[2];
|
|
size_t leftover;
|
|
unsigned char buffer[poly1305_block_size];
|
|
unsigned char final;
|
|
} poly1305_state_internal_t;
|
|
|
|
/* interpret eight 8 bit unsigned integers as a 64 bit unsigned integer in
|
|
* little endian */
|
|
static uint64_t U8TO64(const unsigned char *p) {
|
|
return (((uint64_t) (p[0] & 0xff)) | ((uint64_t) (p[1] & 0xff) << 8) |
|
|
((uint64_t) (p[2] & 0xff) << 16) | ((uint64_t) (p[3] & 0xff) << 24) |
|
|
((uint64_t) (p[4] & 0xff) << 32) | ((uint64_t) (p[5] & 0xff) << 40) |
|
|
((uint64_t) (p[6] & 0xff) << 48) | ((uint64_t) (p[7] & 0xff) << 56));
|
|
}
|
|
|
|
/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little
|
|
* endian */
|
|
static void U64TO8(unsigned char *p, uint64_t v) {
|
|
p[0] = (unsigned char) ((v) & 0xff);
|
|
p[1] = (unsigned char) ((v >> 8) & 0xff);
|
|
p[2] = (unsigned char) ((v >> 16) & 0xff);
|
|
p[3] = (unsigned char) ((v >> 24) & 0xff);
|
|
p[4] = (unsigned char) ((v >> 32) & 0xff);
|
|
p[5] = (unsigned char) ((v >> 40) & 0xff);
|
|
p[6] = (unsigned char) ((v >> 48) & 0xff);
|
|
p[7] = (unsigned char) ((v >> 56) & 0xff);
|
|
}
|
|
|
|
static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
uint64_t t0, t1;
|
|
|
|
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
|
t0 = U8TO64(&key[0]);
|
|
t1 = U8TO64(&key[8]);
|
|
|
|
st->r[0] = (t0) & 0xffc0fffffff;
|
|
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
|
|
st->r[2] = ((t1 >> 24)) & 0x00ffffffc0f;
|
|
|
|
/* h = 0 */
|
|
st->h[0] = 0;
|
|
st->h[1] = 0;
|
|
st->h[2] = 0;
|
|
|
|
/* save pad for later */
|
|
st->pad[0] = U8TO64(&key[16]);
|
|
st->pad[1] = U8TO64(&key[24]);
|
|
|
|
st->leftover = 0;
|
|
st->final = 0;
|
|
}
|
|
|
|
static void poly1305_blocks(poly1305_state_internal_t *st,
|
|
const unsigned char *m, size_t bytes) {
|
|
const uint64_t hibit = (st->final) ? 0 : ((uint64_t) 1 << 40); /* 1 << 128 */
|
|
uint64_t r0, r1, r2;
|
|
uint64_t s1, s2;
|
|
uint64_t h0, h1, h2;
|
|
uint64_t c;
|
|
uint128_t d0, d1, d2, d;
|
|
|
|
r0 = st->r[0];
|
|
r1 = st->r[1];
|
|
r2 = st->r[2];
|
|
|
|
h0 = st->h[0];
|
|
h1 = st->h[1];
|
|
h2 = st->h[2];
|
|
|
|
s1 = r1 * (5 << 2);
|
|
s2 = r2 * (5 << 2);
|
|
|
|
while (bytes >= poly1305_block_size) {
|
|
uint64_t t0, t1;
|
|
|
|
/* h += m[i] */
|
|
t0 = U8TO64(&m[0]);
|
|
t1 = U8TO64(&m[8]);
|
|
|
|
h0 += ((t0) & 0xfffffffffff);
|
|
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
|
|
h2 += (((t1 >> 24)) & 0x3ffffffffff) | hibit;
|
|
|
|
/* h *= r */
|
|
MUL128(d0, h0, r0);
|
|
MUL128(d, h1, s2);
|
|
ADD(d0, d);
|
|
MUL128(d, h2, s1);
|
|
ADD(d0, d);
|
|
MUL128(d1, h0, r1);
|
|
MUL128(d, h1, r0);
|
|
ADD(d1, d);
|
|
MUL128(d, h2, s2);
|
|
ADD(d1, d);
|
|
MUL128(d2, h0, r2);
|
|
MUL128(d, h1, r1);
|
|
ADD(d2, d);
|
|
MUL128(d, h2, r0);
|
|
ADD(d2, d);
|
|
|
|
/* (partial) h %= p */
|
|
c = SHR(d0, 44);
|
|
h0 = LO(d0) & 0xfffffffffff;
|
|
ADDLO(d1, c);
|
|
c = SHR(d1, 44);
|
|
h1 = LO(d1) & 0xfffffffffff;
|
|
ADDLO(d2, c);
|
|
c = SHR(d2, 42);
|
|
h2 = LO(d2) & 0x3ffffffffff;
|
|
h0 += c * 5;
|
|
c = (h0 >> 44);
|
|
h0 = h0 & 0xfffffffffff;
|
|
h1 += c;
|
|
|
|
m += poly1305_block_size;
|
|
bytes -= poly1305_block_size;
|
|
}
|
|
|
|
st->h[0] = h0;
|
|
st->h[1] = h1;
|
|
st->h[2] = h2;
|
|
}
|
|
|
|
static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
|
|
unsigned char mac[16]) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
uint64_t h0, h1, h2, c;
|
|
uint64_t g0, g1, g2;
|
|
uint64_t t0, t1;
|
|
|
|
/* process the remaining block */
|
|
if (st->leftover) {
|
|
size_t i = st->leftover;
|
|
st->buffer[i] = 1;
|
|
for (i = i + 1; i < poly1305_block_size; i++) st->buffer[i] = 0;
|
|
st->final = 1;
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
}
|
|
|
|
/* fully carry h */
|
|
h0 = st->h[0];
|
|
h1 = st->h[1];
|
|
h2 = st->h[2];
|
|
|
|
c = (h1 >> 44);
|
|
h1 &= 0xfffffffffff;
|
|
h2 += c;
|
|
c = (h2 >> 42);
|
|
h2 &= 0x3ffffffffff;
|
|
h0 += c * 5;
|
|
c = (h0 >> 44);
|
|
h0 &= 0xfffffffffff;
|
|
h1 += c;
|
|
c = (h1 >> 44);
|
|
h1 &= 0xfffffffffff;
|
|
h2 += c;
|
|
c = (h2 >> 42);
|
|
h2 &= 0x3ffffffffff;
|
|
h0 += c * 5;
|
|
c = (h0 >> 44);
|
|
h0 &= 0xfffffffffff;
|
|
h1 += c;
|
|
|
|
/* compute h + -p */
|
|
g0 = h0 + 5;
|
|
c = (g0 >> 44);
|
|
g0 &= 0xfffffffffff;
|
|
g1 = h1 + c;
|
|
c = (g1 >> 44);
|
|
g1 &= 0xfffffffffff;
|
|
g2 = h2 + c - ((uint64_t) 1 << 42);
|
|
|
|
/* select h if h < p, or h + -p if h >= p */
|
|
c = (g2 >> ((sizeof(uint64_t) * 8) - 1)) - 1;
|
|
g0 &= c;
|
|
g1 &= c;
|
|
g2 &= c;
|
|
c = ~c;
|
|
h0 = (h0 & c) | g0;
|
|
h1 = (h1 & c) | g1;
|
|
h2 = (h2 & c) | g2;
|
|
|
|
/* h = (h + pad) */
|
|
t0 = st->pad[0];
|
|
t1 = st->pad[1];
|
|
|
|
h0 += ((t0) & 0xfffffffffff);
|
|
c = (h0 >> 44);
|
|
h0 &= 0xfffffffffff;
|
|
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c;
|
|
c = (h1 >> 44);
|
|
h1 &= 0xfffffffffff;
|
|
h2 += (((t1 >> 24)) & 0x3ffffffffff) + c;
|
|
h2 &= 0x3ffffffffff;
|
|
|
|
/* mac = h % (2^128) */
|
|
h0 = ((h0) | (h1 << 44));
|
|
h1 = ((h1 >> 20) | (h2 << 24));
|
|
|
|
U64TO8(&mac[0], h0);
|
|
U64TO8(&mac[8], h1);
|
|
|
|
/* zero out the state */
|
|
st->h[0] = 0;
|
|
st->h[1] = 0;
|
|
st->h[2] = 0;
|
|
st->r[0] = 0;
|
|
st->r[1] = 0;
|
|
st->r[2] = 0;
|
|
st->pad[0] = 0;
|
|
st->pad[1] = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void poly1305_update(poly1305_context *ctx, const unsigned char *m,
|
|
size_t bytes) {
|
|
poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
|
|
size_t i;
|
|
|
|
/* handle leftover */
|
|
if (st->leftover) {
|
|
size_t want = (poly1305_block_size - st->leftover);
|
|
if (want > bytes) want = bytes;
|
|
for (i = 0; i < want; i++) st->buffer[st->leftover + i] = m[i];
|
|
bytes -= want;
|
|
m += want;
|
|
st->leftover += want;
|
|
if (st->leftover < poly1305_block_size) return;
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
st->leftover = 0;
|
|
}
|
|
|
|
/* process full blocks */
|
|
if (bytes >= poly1305_block_size) {
|
|
size_t want = (bytes & (size_t) ~(poly1305_block_size - 1));
|
|
poly1305_blocks(st, m, want);
|
|
m += want;
|
|
bytes -= want;
|
|
}
|
|
|
|
/* store leftover */
|
|
if (bytes) {
|
|
for (i = 0; i < bytes; i++) st->buffer[st->leftover + i] = m[i];
|
|
st->leftover += bytes;
|
|
}
|
|
}
|
|
|
|
// ******* END: poly1305-donna.c ********
|
|
// ******* BEGIN: portable8439.c ********
|
|
|
|
#define __CHACHA20_BLOCK_SIZE (64)
|
|
#define __POLY1305_KEY_SIZE (32)
|
|
|
|
static PORTABLE_8439_DECL uint8_t __ZEROES[16] = {0};
|
|
static PORTABLE_8439_DECL void pad_if_needed(poly1305_context *ctx,
|
|
size_t size) {
|
|
size_t padding = size % 16;
|
|
if (padding != 0) {
|
|
poly1305_update(ctx, __ZEROES, 16 - padding);
|
|
}
|
|
}
|
|
|
|
#define __u8(v) ((uint8_t) ((v) & 0xFF))
|
|
|
|
// TODO: make this depending on the unaligned/native read size possible
|
|
static PORTABLE_8439_DECL void write_64bit_int(poly1305_context *ctx,
|
|
uint64_t value) {
|
|
uint8_t result[8];
|
|
result[0] = __u8(value);
|
|
result[1] = __u8(value >> 8);
|
|
result[2] = __u8(value >> 16);
|
|
result[3] = __u8(value >> 24);
|
|
result[4] = __u8(value >> 32);
|
|
result[5] = __u8(value >> 40);
|
|
result[6] = __u8(value >> 48);
|
|
result[7] = __u8(value >> 56);
|
|
poly1305_update(ctx, result, 8);
|
|
}
|
|
|
|
static PORTABLE_8439_DECL void poly1305_calculate_mac(
|
|
uint8_t *mac, const uint8_t *cipher_text, size_t cipher_text_size,
|
|
const uint8_t key[RFC_8439_KEY_SIZE],
|
|
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *ad,
|
|
size_t ad_size) {
|
|
// init poly key (section 2.6)
|
|
uint8_t poly_key[__POLY1305_KEY_SIZE] = {0};
|
|
poly1305_context poly_ctx;
|
|
rfc8439_keygen(poly_key, key, nonce);
|
|
// start poly1305 mac
|
|
poly1305_init(&poly_ctx, poly_key);
|
|
|
|
if (ad != NULL && ad_size > 0) {
|
|
// write AD if present
|
|
poly1305_update(&poly_ctx, ad, ad_size);
|
|
pad_if_needed(&poly_ctx, ad_size);
|
|
}
|
|
|
|
// now write the cipher text
|
|
poly1305_update(&poly_ctx, cipher_text, cipher_text_size);
|
|
pad_if_needed(&poly_ctx, cipher_text_size);
|
|
|
|
// write sizes
|
|
write_64bit_int(&poly_ctx, ad_size);
|
|
write_64bit_int(&poly_ctx, cipher_text_size);
|
|
|
|
// calculate MAC
|
|
poly1305_finish(&poly_ctx, mac);
|
|
}
|
|
|
|
#define MG_PM(p) ((size_t) (p))
|
|
|
|
// pointers overlap if the smaller either ahead of the end,
|
|
// or its end is before the start of the other
|
|
//
|
|
// s_size should be smaller or equal to b_size
|
|
#define MG_OVERLAPPING(s, s_size, b, b_size) \
|
|
(MG_PM(s) < MG_PM((b) + (b_size))) && (MG_PM(b) < MG_PM((s) + (s_size)))
|
|
|
|
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
|
|
uint8_t *restrict cipher_text, const uint8_t key[RFC_8439_KEY_SIZE],
|
|
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
|
|
size_t ad_size, const uint8_t *restrict plain_text,
|
|
size_t plain_text_size) {
|
|
size_t new_size = plain_text_size + RFC_8439_TAG_SIZE;
|
|
if (MG_OVERLAPPING(plain_text, plain_text_size, cipher_text, new_size)) {
|
|
return (size_t) -1;
|
|
}
|
|
chacha20_xor_stream(cipher_text, plain_text, plain_text_size, key, nonce, 1);
|
|
poly1305_calculate_mac(cipher_text + plain_text_size, cipher_text,
|
|
plain_text_size, key, nonce, ad, ad_size);
|
|
return new_size;
|
|
}
|
|
|
|
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
|
|
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
|
|
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
|
|
size_t ad_size, const uint8_t *restrict cipher_text,
|
|
size_t cipher_text_size) {
|
|
// first we calculate the mac and see if it lines up, only then do we decrypt
|
|
size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
|
|
uint8_t computed_mac[RFC_8439_TAG_SIZE];
|
|
int diff = 0;
|
|
size_t i;
|
|
if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
|
|
return (size_t) -1;
|
|
}
|
|
|
|
poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
|
|
ad_size);
|
|
|
|
// compare tags
|
|
for (i = 0; i < RFC_8439_TAG_SIZE; i++)
|
|
diff |= computed_mac[i] ^ cipher_text[actual_size + i];
|
|
if (diff != 0) return (size_t) -1;
|
|
|
|
chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
|
|
return actual_size;
|
|
}
|
|
// ******* END: portable8439.c ********
|
|
#endif // MG_TLS == MG_TLS_BUILTIN
|