#if DEBUG
#include <assert.h>
#endif
#include <string.h>
#include "KangarooTwelve.h"
#include "KeccakP-1600-SnP.h"
static void TurboSHAKE_Initialize(TurboSHAKE_Instance *instance, unsigned int capacity)
{
KeccakP1600_Initialize(instance->state);
instance->rate = 1600 - capacity;
instance->byteIOIndex = 0;
instance->squeezing = 0;
}
static void TurboSHAKE_Absorb(TurboSHAKE_Instance *instance, const unsigned char *data, size_t dataByteLen)
{
size_t i, j;
uint8_t partialBlock;
const unsigned char *curData;
const uint8_t rateInBytes = instance->rate/8;
#if DEBUG
assert(instance->squeezing == 0);
#endif
i = 0;
curData = data;
while(i < dataByteLen) {
if ((instance->byteIOIndex == 0) && (dataByteLen-i >= rateInBytes)) {
#ifdef KeccakP1600_12rounds_FastLoop_supported
j = KeccakP1600_12rounds_FastLoop_Absorb(instance->state, instance->rate/64, curData, dataByteLen - i);
i += j;
curData += j;
#endif
for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) {
KeccakP1600_AddBytes(instance->state, curData, 0, rateInBytes);
KeccakP1600_Permute_12rounds(instance->state);
curData+=rateInBytes;
}
i = dataByteLen - j;
} else {
if (dataByteLen - i > (size_t)rateInBytes - instance->byteIOIndex) {
partialBlock = rateInBytes-instance->byteIOIndex;
} else {
partialBlock = (uint8_t)(dataByteLen - i);
}
i += partialBlock;
KeccakP1600_AddBytes(instance->state, curData, instance->byteIOIndex, partialBlock);
curData += partialBlock;
instance->byteIOIndex += partialBlock;
if (instance->byteIOIndex == rateInBytes) {
KeccakP1600_Permute_12rounds(instance->state);
instance->byteIOIndex = 0;
}
}
}
}
static void TurboSHAKE_AbsorbDomainSeparationByte(TurboSHAKE_Instance *instance, unsigned char D)
{
const unsigned int rateInBytes = instance->rate/8;
#if DEBUG
assert(D != 0);
assert(instance->squeezing == 0);
#endif
KeccakP1600_AddByte(instance->state, D, instance->byteIOIndex);
if ((D >= 0x80) && (instance->byteIOIndex == (rateInBytes-1)))
KeccakP1600_Permute_12rounds(instance->state);
KeccakP1600_AddByte(instance->state, 0x80, rateInBytes-1);
KeccakP1600_Permute_12rounds(instance->state);
instance->byteIOIndex = 0;
instance->squeezing = 1;
}
static void TurboSHAKE_Squeeze(TurboSHAKE_Instance *instance, unsigned char *data, size_t dataByteLen)
{
size_t i, j;
unsigned int partialBlock;
const unsigned int rateInBytes = instance->rate/8;
unsigned char *curData;
if (!instance->squeezing)
TurboSHAKE_AbsorbDomainSeparationByte(instance, 0x01);
i = 0;
curData = data;
while(i < dataByteLen) {
if ((instance->byteIOIndex == rateInBytes) && (dataByteLen-i >= rateInBytes)) {
for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) {
KeccakP1600_Permute_12rounds(instance->state);
KeccakP1600_ExtractBytes(instance->state, curData, 0, rateInBytes);
curData+=rateInBytes;
}
i = dataByteLen - j;
} else {
if (instance->byteIOIndex == rateInBytes) {
KeccakP1600_Permute_12rounds(instance->state);
instance->byteIOIndex = 0;
}
if (dataByteLen-i > rateInBytes-instance->byteIOIndex)
partialBlock = rateInBytes-instance->byteIOIndex;
else
partialBlock = (unsigned int)(dataByteLen - i);
i += partialBlock;
KeccakP1600_ExtractBytes(instance->state, curData, instance->byteIOIndex, partialBlock);
curData += partialBlock;
instance->byteIOIndex += partialBlock;
}
}
}
typedef enum {
NOT_INITIALIZED,
ABSORBING,
FINAL,
SQUEEZING
} KCP_Phases;
typedef KCP_Phases KangarooTwelve_Phases;
#define K12_chunkSize 8192
#define K12_suffixLeaf 0x0B
#define KT128_capacityInBytes 32
#define KT256_capacityInBytes 64
#define maxCapacityInBytes 64
#ifndef KeccakP1600_disableParallelism
void KT128_Process2Leaves(const unsigned char *input, unsigned char *output);
void KT128_Process4Leaves(const unsigned char *input, unsigned char *output);
void KT128_Process8Leaves(const unsigned char *input, unsigned char *output);
void KT256_Process2Leaves(const unsigned char *input, unsigned char *output);
void KT256_Process4Leaves(const unsigned char *input, unsigned char *output);
void KT256_Process8Leaves(const unsigned char *input, unsigned char *output);
#define ProcessLeaves( Parallellism, CapacityInBytes ) \
if( CapacityInBytes == KT128_capacityInBytes ) { \
while (inputByteLen >= Parallellism * K12_chunkSize) { \
unsigned char intermediate[Parallellism * KT128_capacityInBytes]; \
\
KT128_Process##Parallellism##Leaves(input, intermediate); \
input += Parallellism * K12_chunkSize; \
inputByteLen -= Parallellism * K12_chunkSize; \
ktInstance->blockNumber += Parallellism; \
TurboSHAKE_Absorb(&ktInstance->finalNode, intermediate, Parallellism * CapacityInBytes); \
} \
} \
else if( CapacityInBytes == KT256_capacityInBytes ) { \
while (inputByteLen >= Parallellism * K12_chunkSize) { \
unsigned char intermediate[Parallellism * KT256_capacityInBytes]; \
\
KT256_Process##Parallellism##Leaves(input, intermediate); \
input += Parallellism * K12_chunkSize; \
inputByteLen -= Parallellism * K12_chunkSize; \
ktInstance->blockNumber += Parallellism; \
TurboSHAKE_Absorb(&ktInstance->finalNode, intermediate, Parallellism * CapacityInBytes); \
} \
}
#endif
static unsigned int right_encode(unsigned char * encbuf, size_t value)
{
unsigned int n, i;
size_t v;
for (v = value, n = 0; v && (n < sizeof(size_t)); ++n, v >>= 8)
;
for (i = 1; i <= n; ++i) {
encbuf[i-1] = (unsigned char)(value >> (8 * (n-i)));
}
encbuf[n] = (unsigned char)n;
return n + 1;
}
int KangarooTwelve_Initialize(KangarooTwelve_Instance *ktInstance, int securityLevel, size_t outputByteLen)
{
if ((securityLevel != 128) && (securityLevel != 256))
return 1;
ktInstance->fixedOutputLength = outputByteLen;
ktInstance->queueAbsorbedLen = 0;
ktInstance->blockNumber = 0;
ktInstance->phase = ABSORBING;
ktInstance->securityLevel = securityLevel;
TurboSHAKE_Initialize(&ktInstance->finalNode, 2*securityLevel);
return 0;
}
int KangarooTwelve_Update(KangarooTwelve_Instance *ktInstance, const unsigned char *input, size_t inputByteLen)
{
if (ktInstance->phase != ABSORBING)
return 1;
if (ktInstance->blockNumber == 0) {
unsigned int len = (inputByteLen < (K12_chunkSize - ktInstance->queueAbsorbedLen)) ? (unsigned int)inputByteLen : (K12_chunkSize - ktInstance->queueAbsorbedLen);
TurboSHAKE_Absorb(&ktInstance->finalNode, input, len);
input += len;
inputByteLen -= len;
ktInstance->queueAbsorbedLen += len;
if ((ktInstance->queueAbsorbedLen == K12_chunkSize) && (inputByteLen != 0)) {
const unsigned char padding = 0x03;
ktInstance->queueAbsorbedLen = 0;
ktInstance->blockNumber = 1;
TurboSHAKE_Absorb(&ktInstance->finalNode, &padding, 1);
ktInstance->finalNode.byteIOIndex = (ktInstance->finalNode.byteIOIndex + 7) & ~7;
}
} else if (ktInstance->queueAbsorbedLen != 0) {
unsigned int len = (inputByteLen < (K12_chunkSize - ktInstance->queueAbsorbedLen)) ? (unsigned int)inputByteLen : (K12_chunkSize - ktInstance->queueAbsorbedLen);
TurboSHAKE_Absorb(&ktInstance->queueNode, input, len);
input += len;
inputByteLen -= len;
ktInstance->queueAbsorbedLen += len;
if (ktInstance->queueAbsorbedLen == K12_chunkSize) {
int capacityInBytes = 2*(ktInstance->securityLevel)/8;
unsigned char intermediate[maxCapacityInBytes];
#if DEBUG
assert(capacityInBytes <= maxCapacityInBytes);
#endif
ktInstance->queueAbsorbedLen = 0;
++ktInstance->blockNumber;
TurboSHAKE_AbsorbDomainSeparationByte(&ktInstance->queueNode, K12_suffixLeaf);
TurboSHAKE_Squeeze(&ktInstance->queueNode, intermediate, capacityInBytes);
TurboSHAKE_Absorb(&ktInstance->finalNode, intermediate, capacityInBytes);
}
}
int capacityInBytes = 2*(ktInstance->securityLevel)/8;
#ifndef KeccakP1600_disableParallelism
if (KeccakP1600times8_IsAvailable()) {
ProcessLeaves(8, capacityInBytes);
}
if (KeccakP1600times4_IsAvailable()) {
ProcessLeaves(4, capacityInBytes);
}
if (KeccakP1600times2_IsAvailable()) {
ProcessLeaves(2, capacityInBytes);
}
#endif
while (inputByteLen > 0) {
unsigned int len = (inputByteLen < K12_chunkSize) ? (unsigned int)inputByteLen : K12_chunkSize;
TurboSHAKE_Initialize(&ktInstance->queueNode, 2*ktInstance->securityLevel);
TurboSHAKE_Absorb(&ktInstance->queueNode, input, len);
input += len;
inputByteLen -= len;
if (len == K12_chunkSize) {
capacityInBytes = 2*(ktInstance->securityLevel)/8;
unsigned char intermediate[maxCapacityInBytes];
#if DEBUG
assert(capacityInBytes <= maxCapacityInBytes);
#endif
++ktInstance->blockNumber;
TurboSHAKE_AbsorbDomainSeparationByte(&ktInstance->queueNode, K12_suffixLeaf);
TurboSHAKE_Squeeze(&ktInstance->queueNode, intermediate, capacityInBytes);
TurboSHAKE_Absorb(&ktInstance->finalNode, intermediate, capacityInBytes);
} else {
ktInstance->queueAbsorbedLen = len;
}
}
return 0;
}
int KangarooTwelve_Final(KangarooTwelve_Instance *ktInstance, unsigned char *output, const unsigned char *customization, size_t customByteLen)
{
unsigned char encbuf[sizeof(size_t)+1+2];
unsigned char padding;
if (ktInstance->phase != ABSORBING)
return 1;
if ((customByteLen != 0) && (KangarooTwelve_Update(ktInstance, customization, customByteLen) != 0))
return 1;
if (KangarooTwelve_Update(ktInstance, encbuf, right_encode(encbuf, customByteLen)) != 0)
return 1;
if (ktInstance->blockNumber == 0) {
padding = 0x07;
} else {
unsigned int n;
if (ktInstance->queueAbsorbedLen != 0) {
int capacityInBytes = 2*(ktInstance->securityLevel)/8;
unsigned char intermediate[maxCapacityInBytes];
#if DEBUG
assert(capacityInBytes <= maxCapacityInBytes);
#endif
++ktInstance->blockNumber;
TurboSHAKE_AbsorbDomainSeparationByte(&ktInstance->queueNode, K12_suffixLeaf);
TurboSHAKE_Squeeze(&ktInstance->queueNode, intermediate, capacityInBytes);
TurboSHAKE_Absorb(&ktInstance->finalNode, intermediate, capacityInBytes);
}
--ktInstance->blockNumber;
n = right_encode(encbuf, ktInstance->blockNumber);
encbuf[n++] = 0xFF;
encbuf[n++] = 0xFF;
TurboSHAKE_Absorb(&ktInstance->finalNode, encbuf, n);
padding = 0x06;
}
TurboSHAKE_AbsorbDomainSeparationByte(&ktInstance->finalNode, padding);
if (ktInstance->fixedOutputLength != 0) {
ktInstance->phase = FINAL;
TurboSHAKE_Squeeze(&ktInstance->finalNode, output, ktInstance->fixedOutputLength);
return 0;
}
ktInstance->phase = SQUEEZING;
return 0;
}
int KangarooTwelve_Squeeze(KangarooTwelve_Instance *ktInstance, unsigned char *output, size_t outputByteLen)
{
if (ktInstance->phase != SQUEEZING)
return 1;
TurboSHAKE_Squeeze(&ktInstance->finalNode, output, outputByteLen);
return 0;
}
int KangarooTwelve(int securityLevel, const unsigned char *input, size_t inputByteLen,
unsigned char *output, size_t outputByteLen,
const unsigned char *customization, size_t customByteLen)
{
KangarooTwelve_Instance ktInstance;
if (outputByteLen == 0)
return 1;
KangarooTwelve_Initialize(&ktInstance, securityLevel, outputByteLen);
if (KangarooTwelve_Update(&ktInstance, input, inputByteLen) != 0)
return 1;
return KangarooTwelve_Final(&ktInstance, output, customization, customByteLen);
}
int KT128(const unsigned char *input, size_t inputByteLen,
unsigned char *output, size_t outputByteLen,
const unsigned char *customization, size_t customByteLen)
{
return KangarooTwelve(128, input, inputByteLen, output, outputByteLen, customization, customByteLen);
}
int KT256(const unsigned char *input, size_t inputByteLen,
unsigned char *output, size_t outputByteLen,
const unsigned char *customization, size_t customByteLen)
{
return KangarooTwelve(256, input, inputByteLen, output, outputByteLen, customization, customByteLen);
}