pkgsrc/security/multigest/files/keccak.c
agc 7e5a773aa6 Update multigest and libmultigest to version 20150211
+ bring over lint fixes from the version in othersrc
+ document the concat, comb4p, xor and hash combiner functions
2015-02-12 01:57:57 +00:00

411 lines
10 KiB
C

/*-
* Copyright (c) 2013 Alistair Crooks <agc@NetBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
Michaël Peeters and Gilles Van Assche. For more information, feedback or
questions, please refer to our website: http://keccak.noekeon.org/
Implementation by the designers,
hereby denoted as "the implementer".
To the extent possible under law, the implementer has waived all copyright
and related or neighboring rights to the source code in this file.
http://creativecommons.org/publicdomain/zero/1.0/
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "keccak.h"
static void
fromBytesToWords(uint64_t *stateAsWords, const uint8_t *state)
{
unsigned int i, j;
for (i = 0; i < (KECCAK_PERMUTATION_SIZE_BITS / 64); i++) {
stateAsWords[i] = 0;
for (j = 0; j < (64/8); j++) {
stateAsWords[i] |= (uint64_t)(state[i*(64/8)+j]) << (8*j);
}
}
}
static void
fromWordsToBytes(uint8_t *state, const uint64_t *stateAsWords)
{
unsigned int i, j;
for (i = 0; i < (KECCAK_PERMUTATION_SIZE_BITS / 64); i++) {
for (j = 0; j < (64/8); j++) {
state[i*(64/8)+j] = (uint8_t)(stateAsWords[i] >> (8*j)) & 0xFF;
}
}
}
#define INDEX(x, y) (((x)%5)+5*((y)%5))
#define ROL64(a, offset) (/*CONSTCOND*/(offset != 0) ? ((((uint64_t)a) << offset) ^ (((uint64_t)a) >> (64-offset))) : a)
static inline void
theta(uint64_t *A)
{
unsigned int x, y;
uint64_t C[5], D[5];
for (x = 0; x < 5; x++) {
C[x] = 0;
for (y = 0; y < 5; y++) {
C[x] ^= A[INDEX(x, y)];
}
}
for (x = 0; x < 5; x++) {
D[x] = ROL64(C[(x+1)%5], 1) ^ C[(x+4)%5];
}
for (x = 0; x < 5; x++) {
for (y = 0; y < 5; y++) {
A[INDEX(x, y)] ^= D[x];
}
}
}
static inline void
rho(KECCAK_CTX *ctx, uint64_t *A)
{
unsigned int x, y;
for (x = 0; x < 5; x++) {
for (y = 0; y < 5; y++) {
A[INDEX(x, y)] = ROL64(A[INDEX(x, y)], ctx->RhoOffsets[INDEX(x, y)]);
}
}
}
static inline void
pi(uint64_t *A)
{
unsigned int x, y;
uint64_t tempA[25];
for (x = 0; x < 5; x++) {
for (y = 0; y < 5; y++) {
tempA[INDEX(x, y)] = A[INDEX(x, y)];
}
}
for (x = 0; x < 5; x++) {
for (y = 0; y < 5; y++) {
A[INDEX(0*x+1*y, 2*x+3*y)] = tempA[INDEX(x, y)];
}
}
}
static inline void
chi(uint64_t *A)
{
unsigned int x, y;
uint64_t C[5];
for (y = 0; y < 5; y++) {
for (x = 0; x < 5; x++) {
C[x] = A[INDEX(x, y)] ^ ((~A[INDEX(x+1, y)]) & A[INDEX(x+2, y)]);
}
for (x = 0; x < 5; x++) {
A[INDEX(x, y)] = C[x];
}
}
}
static inline void
iota(KECCAK_CTX *ctx, uint64_t *A, unsigned int indexRound)
{
A[INDEX(0, 0)] ^= ctx->RoundConstants[indexRound];
}
static inline void
KeccakPermutationOnWords(KECCAK_CTX *ctx, uint64_t *st)
{
unsigned int i;
for (i = 0; i < KECCAK_NUM_ROUNDS; i++) {
theta(st);
rho(ctx, st);
pi(st);
chi(st);
iota(ctx, st, i);
}
}
static void
keccak_permutation(KECCAK_CTX *ctx)
{
const int indian = 1;
uint64_t stateAsWords[KECCAK_PERMUTATION_SIZE_BITS/64];
if (*(const char *)(const void *)&indian) {
/* little endian */
KeccakPermutationOnWords(ctx, (uint64_t*)(void *)ctx->state);
} else {
fromBytesToWords(stateAsWords, ctx->state);
KeccakPermutationOnWords(ctx, stateAsWords);
fromWordsToBytes(ctx->state, stateAsWords);
}
}
static void
KeccakPermutationAfterXor(KECCAK_CTX *ctx, uint8_t *state, const uint8_t *data, unsigned int dataLengthInBytes)
{
unsigned int i;
for (i = 0; i < dataLengthInBytes; i++) {
state[i] ^= data[i];
}
keccak_permutation(ctx);
}
static int
LFSR86540(uint8_t *LFSR)
{
int result = ((*LFSR) & 0x01) != 0;
if (((*LFSR) & 0x80) != 0) {
/* Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 */
(*LFSR) = (uint8_t)((*LFSR) << 1) ^ 0x71;
} else {
(*LFSR) = (uint8_t)((*LFSR) << 1);
}
return result;
}
static void
keccak_initialise_RoundConstants(KECCAK_CTX *ctx)
{
uint8_t LFSRstate = 0x01;
unsigned int i, j, bitPosition;
for (i = 0; i < KECCAK_NUM_ROUNDS; i++) {
ctx->RoundConstants[i] = 0;
for (j = 0; j < 7; j++) {
bitPosition = (unsigned)(1<<j)-1; /*2^j-1 */
if (LFSR86540(&LFSRstate)) {
ctx->RoundConstants[i] ^= (uint64_t)1<<bitPosition;
}
}
}
}
static void
keccak_initialise_RhoOffsets(KECCAK_CTX *ctx)
{
unsigned int x, y, t, newX, newY;
ctx->RhoOffsets[INDEX(0, 0)] = 0;
x = 1;
y = 0;
for (t = 0; t < 24; t++) {
ctx->RhoOffsets[INDEX(x, y)] = ((t+1)*(t+2)/2) % 64;
newX = (0*x+1*y) % 5;
newY = (2*x+3*y) % 5;
x = newX;
y = newY;
}
}
static int
init_sponge(KECCAK_CTX *ctx, unsigned int rate, unsigned int capacity)
{
if (rate+capacity != 1600) {
return 1;
}
if ((rate == 0) || (rate >= 1600) || ((rate % 64) != 0)) {
return 1;
}
keccak_initialise_RoundConstants(ctx);
keccak_initialise_RhoOffsets(ctx);
ctx->rate = rate;
ctx->capacity = capacity;
ctx->fixedOutputLength = 0;
memset(ctx->state, 0x0, sizeof(ctx->state));
memset(ctx->dataQueue, 0x0, sizeof(ctx->dataQueue));
ctx->bitsInQueue = 0;
ctx->squeezing = 0;
ctx->bitsAvailableForSqueezing = 0;
return 0;
}
static inline void
absorb_queue(KECCAK_CTX *ctx)
{
/* state->bitsInQueue is assumed to be equal to state->rate */
KeccakPermutationAfterXor(ctx, ctx->state, ctx->dataQueue, ctx->rate / 8);
ctx->bitsInQueue = 0;
}
static int
absorb(KECCAK_CTX *ctx, const uint8_t *data, uint64_t databitlen)
{
uint64_t i, j, wholeBlocks;
unsigned int partialBlock, partialByte;
const uint8_t *curData;
if ((ctx->bitsInQueue % 8) != 0) {
return 1; /* Only the last call may contain a partial byte */
}
if (ctx->squeezing) {
return 1; /* Too late for additional input */
}
for (i = 0; i < databitlen ; ) {
if ((ctx->bitsInQueue == 0) && (databitlen >= ctx->rate) && (i <= (databitlen-ctx->rate))) {
wholeBlocks = (databitlen-i)/ctx->rate;
curData = &data[(long)i/8];
for (j=0; j<wholeBlocks; j++, curData+=ctx->rate/8) {
KeccakPermutationAfterXor(ctx, ctx->state, curData, ctx->rate / 8);
}
i += wholeBlocks*ctx->rate;
} else {
partialBlock = (unsigned int)(databitlen - i);
if (partialBlock+ctx->bitsInQueue > ctx->rate) {
partialBlock = ctx->rate-ctx->bitsInQueue;
}
partialByte = partialBlock % 8;
partialBlock -= partialByte;
memcpy(ctx->dataQueue+ctx->bitsInQueue/8, &data[(unsigned long)i/8], partialBlock/8);
ctx->bitsInQueue += partialBlock;
i += partialBlock;
if (ctx->bitsInQueue == ctx->rate) {
absorb_queue(ctx);
}
if (partialByte > 0) {
uint8_t mask = (uint8_t)((1 << partialByte)-1);
ctx->dataQueue[ctx->bitsInQueue/8] = data[(unsigned long)i/8] & mask;
ctx->bitsInQueue += partialByte;
i += partialByte;
}
}
}
return 0;
}
static void
PadAndSwitchToSqueezingPhase(KECCAK_CTX *ctx)
{
/* Note: the bits are numbered from 0=LSB to 7=MSB */
if (ctx->bitsInQueue + 1 == ctx->rate) {
ctx->dataQueue[ctx->bitsInQueue/8 ] |= (uint8_t)(1 << (ctx->bitsInQueue % 8));
absorb_queue(ctx);
memset(ctx->dataQueue, 0, ctx->rate/8);
} else {
memset(ctx->dataQueue + (ctx->bitsInQueue+7)/8, 0, ctx->rate/8 - (ctx->bitsInQueue+7)/8);
ctx->dataQueue[ctx->bitsInQueue/8 ] |= (uint8_t)(1 << (ctx->bitsInQueue % 8));
}
ctx->dataQueue[(ctx->rate-1)/8] |= (uint8_t)(1 << ((ctx->rate-1) % 8));
absorb_queue(ctx);
memcpy(ctx->dataQueue, ctx->state, ctx->rate/8);
ctx->bitsAvailableForSqueezing = ctx->rate;
ctx->squeezing = 1;
}
static int
squeeze(KECCAK_CTX *ctx, uint8_t *output, uint64_t outputLength)
{
uint64_t i;
unsigned int partialBlock;
if (!ctx->squeezing) {
PadAndSwitchToSqueezingPhase(ctx);
}
if ((outputLength % 8) != 0) {
return 1; /* Only multiple of 8 bits are allowed, truncation can be done at user level */
}
for (i = 0; i < outputLength ; i += partialBlock) {
if (ctx->bitsAvailableForSqueezing == 0) {
keccak_permutation(ctx);
memcpy(ctx->dataQueue, ctx->state, ctx->rate/8);
ctx->bitsAvailableForSqueezing = ctx->rate;
}
partialBlock = ctx->bitsAvailableForSqueezing;
if ((uint64_t)partialBlock > outputLength - i) {
partialBlock = (unsigned int)(outputLength - i);
}
memcpy(&output[(unsigned long)i/8],
ctx->dataQueue+(ctx->rate-ctx->bitsAvailableForSqueezing)/8,
partialBlock/8);
ctx->bitsAvailableForSqueezing -= partialBlock;
}
return 0;
}
/***************************************************************/
HashReturn
KECCAK_Init(KECCAK_CTX *ctx, int hashbitlen)
{
switch(hashbitlen) {
case 0: /* Default parameters, arbitrary length output */
init_sponge((KECCAK_CTX*)ctx, 1024, 576);
break;
case 224:
init_sponge((KECCAK_CTX*)ctx, 1152, 448);
break;
case 256:
init_sponge((KECCAK_CTX*)ctx, 1088, 512);
break;
case 384:
init_sponge((KECCAK_CTX*)ctx, 832, 768);
break;
case 512:
init_sponge((KECCAK_CTX*)ctx, 576, 1024);
break;
default:
return BAD_HASHLEN;
}
ctx->fixedOutputLength = (uint32_t)hashbitlen;
return SUCCESS;
}
HashReturn
KECCAK_Update(KECCAK_CTX *ctx, const uint8_t *data, uint64_t databitlen)
{
HashReturn ret;
if ((databitlen % 8) == 0) {
return absorb((KECCAK_CTX*)ctx, data, databitlen);
}
ret = absorb((KECCAK_CTX*)ctx, data, databitlen - (databitlen % 8));
if (ret == SUCCESS) {
uint8_t lastByte;
/* Align the last partial byte to the least significant bits */
lastByte = (uint8_t)(data[(unsigned long)databitlen/8] >> (8 - (databitlen % 8)));
return absorb((KECCAK_CTX*)ctx, &lastByte, databitlen % 8);
}
return ret;
}
HashReturn
KECCAK_Final(KECCAK_CTX *ctx, uint8_t *hashval)
{
return squeeze(ctx, hashval, ctx->fixedOutputLength);
}