198 lines
5.5 KiB
C
198 lines
5.5 KiB
C
/*
|
|
* cifra - embedded cryptography library
|
|
* Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com>
|
|
*
|
|
* To the extent possible under law, the author(s) have dedicated all
|
|
* copyright and related and neighboring rights to this software to the
|
|
* public domain worldwide. This software is distributed without any
|
|
* warranty.
|
|
*
|
|
* You should have received a copy of the CC0 Public Domain Dedication
|
|
* along with this software. If not, see
|
|
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
*/
|
|
|
|
#include "blockwise.h"
|
|
#include "bitops.h"
|
|
#include "tassert.h"
|
|
#include <string.h>
|
|
|
|
#ifndef MIN
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
void cf_blockwise_accumulate(uint8_t *partial, size_t *npartial, size_t nblock,
|
|
const void *inp, size_t nbytes,
|
|
cf_blockwise_in_fn process,
|
|
void *ctx)
|
|
{
|
|
cf_blockwise_accumulate_final(partial, npartial, nblock,
|
|
inp, nbytes,
|
|
process, process, ctx);
|
|
}
|
|
|
|
void cf_blockwise_accumulate_final(uint8_t *partial, size_t *npartial, size_t nblock,
|
|
const void *inp, size_t nbytes,
|
|
cf_blockwise_in_fn process,
|
|
cf_blockwise_in_fn process_final,
|
|
void *ctx)
|
|
{
|
|
const uint8_t *bufin = inp;
|
|
assert(partial && *npartial < nblock);
|
|
assert(inp || !nbytes);
|
|
assert(process && ctx);
|
|
|
|
/* If we have partial data, copy in to buffer. */
|
|
if (*npartial && nbytes)
|
|
{
|
|
size_t space = nblock - *npartial;
|
|
size_t taken = MIN(space, nbytes);
|
|
|
|
memcpy(partial + *npartial, bufin, taken);
|
|
|
|
bufin += taken;
|
|
nbytes -= taken;
|
|
*npartial += taken;
|
|
|
|
/* If that gives us a full block, process it. */
|
|
if (*npartial == nblock)
|
|
{
|
|
if (nbytes == 0)
|
|
process_final(ctx, partial);
|
|
else
|
|
process(ctx, partial);
|
|
*npartial = 0;
|
|
}
|
|
}
|
|
|
|
/* now nbytes < nblock or *npartial == 0. */
|
|
|
|
/* If we have a full block of data, process it directly. */
|
|
while (nbytes >= nblock)
|
|
{
|
|
/* Partial buffer must be empty, or we're ignoring extant data */
|
|
assert(*npartial == 0);
|
|
|
|
if (nbytes == nblock)
|
|
process_final(ctx, bufin);
|
|
else
|
|
process(ctx, bufin);
|
|
bufin += nblock;
|
|
nbytes -= nblock;
|
|
}
|
|
|
|
/* Finally, if we have remaining data, buffer it. */
|
|
while (nbytes)
|
|
{
|
|
size_t space = nblock - *npartial;
|
|
size_t taken = MIN(space, nbytes);
|
|
|
|
memcpy(partial + *npartial, bufin, taken);
|
|
|
|
bufin += taken;
|
|
nbytes -= taken;
|
|
*npartial += taken;
|
|
|
|
/* If we started with *npartial, we must have copied it
|
|
* in first. */
|
|
assert(*npartial < nblock);
|
|
}
|
|
}
|
|
|
|
void cf_blockwise_xor(uint8_t *partial, size_t *npartial, size_t nblock,
|
|
const void *inp, void *outp, size_t nbytes,
|
|
cf_blockwise_out_fn process, void *ctx)
|
|
{
|
|
const uint8_t *inb = inp;
|
|
uint8_t *outb = outp;
|
|
|
|
assert(partial && *npartial < nblock);
|
|
assert(inp || !nbytes);
|
|
assert(process && ctx);
|
|
|
|
while (nbytes)
|
|
{
|
|
/* If we're out of material, and need more, produce a block. */
|
|
if (*npartial == 0)
|
|
{
|
|
process(ctx, partial);
|
|
*npartial = nblock;
|
|
}
|
|
|
|
size_t offset = nblock - *npartial;
|
|
size_t taken = MIN(*npartial, nbytes);
|
|
xor_bb(outb, inb, partial + offset, taken);
|
|
*npartial -= taken;
|
|
nbytes -= taken;
|
|
outb += taken;
|
|
inb += taken;
|
|
}
|
|
}
|
|
|
|
void cf_blockwise_acc_byte(uint8_t *partial, size_t *npartial,
|
|
size_t nblock,
|
|
uint8_t byte, size_t nbytes,
|
|
cf_blockwise_in_fn process,
|
|
void *ctx)
|
|
{
|
|
/* only memset the whole of the block once */
|
|
int filled = 0;
|
|
|
|
while (nbytes)
|
|
{
|
|
size_t start = *npartial;
|
|
size_t count = MIN(nbytes, nblock - start);
|
|
|
|
if (!filled)
|
|
memset(partial + start, byte, count);
|
|
|
|
if (start == 0 && count == nblock)
|
|
filled = 1;
|
|
|
|
if (start + count == nblock)
|
|
{
|
|
process(ctx, partial);
|
|
*npartial = 0;
|
|
} else {
|
|
*npartial += count;
|
|
}
|
|
|
|
nbytes -= count;
|
|
}
|
|
}
|
|
|
|
void cf_blockwise_acc_pad(uint8_t *partial, size_t *npartial,
|
|
size_t nblock,
|
|
uint8_t fbyte, uint8_t mbyte, uint8_t lbyte,
|
|
size_t nbytes,
|
|
cf_blockwise_in_fn process,
|
|
void *ctx)
|
|
{
|
|
|
|
switch (nbytes)
|
|
{
|
|
case 0: break;
|
|
case 1: fbyte ^= lbyte;
|
|
cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx);
|
|
break;
|
|
case 2:
|
|
cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx);
|
|
cf_blockwise_accumulate(partial, npartial, nblock, &lbyte, 1, process, ctx);
|
|
break;
|
|
default:
|
|
cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx);
|
|
|
|
/* If the middle and last bytes differ, then process the last byte separately.
|
|
* Otherwise, just extend the middle block size. */
|
|
if (lbyte != mbyte)
|
|
{
|
|
cf_blockwise_acc_byte(partial, npartial, nblock, mbyte, nbytes - 2, process, ctx);
|
|
cf_blockwise_accumulate(partial, npartial, nblock, &lbyte, 1, process, ctx);
|
|
} else {
|
|
cf_blockwise_acc_byte(partial, npartial, nblock, mbyte, nbytes - 1, process, ctx);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|