Expose an incremental API in addition to the one-shot routines.

The digest routines use init/update/sum, where sum will automatically
copy the internal state to support calculating running digests.

The XOF routines use init/absorb/squeeze, which behave exactly as stated
on the tin.
This commit is contained in:
Yawning Angel 2015-12-18 21:15:52 +00:00
parent e993003792
commit 18685df031
2 changed files with 279 additions and 25 deletions

View File

@ -102,46 +102,253 @@ mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
mkapply_sd(setout, dst[i] = src[i]) // setout
#define P keccakf
#define Plen 200
#define Plen KECCAK_MAX_RATE
#define KECCAK_DELIM_DIGEST 0x06
#define KECCAK_DELIM_XOF 0x1f
// Fold P*F over the full blocks of an input.
#define foldP(I, L, F) \
while (L >= rate) { \
F(a, I, rate); \
P(a); \
I += rate; \
L -= rate; \
while (L >= s->rate) { \
F(s->a, I, s->rate); \
P(s->a); \
I += s->rate; \
L -= s->rate; \
}
static inline void
keccak_absorb_blocks(keccak_state *s, const uint8_t *buf, size_t nr_blocks)
{
size_t blen = nr_blocks * s->rate;
foldP(buf, blen, xorin);
}
static int
keccak_update(keccak_state *s, const uint8_t *buf, size_t len)
{
if (s->finalized)
return -1;
if ((buf == NULL) && len != 0)
return -1;
size_t remaining = len;
while (remaining > 0) {
if (s->offset == 0) {
const size_t blocks = remaining / s->rate;
size_t direct_bytes = blocks * s->rate;
if (direct_bytes > 0) {
keccak_absorb_blocks(s, buf, blocks);
remaining -= direct_bytes;
buf += direct_bytes;
}
}
const size_t buf_avail = s->rate - s->offset;
const size_t buf_bytes = (buf_avail > remaining) ? remaining : buf_avail;
if (buf_bytes > 0) {
memcpy(&s->block[s->offset], buf, buf_bytes);
s->offset += buf_bytes;
remaining -= buf_bytes;
buf += buf_bytes;
}
if (s->offset == s->rate) {
keccak_absorb_blocks(s, s->block, 1);
s->offset = 0;
}
}
return 0;
}
static void
keccak_finalize(keccak_state *s)
{
// Xor in the DS and pad frame.
s->a[s->offset] ^= s->delim;
s->a[s->rate - 1] ^= 0x80;
// Xor in the last block.
xorin(s->a, s->block, s->offset);
memset_s(s->block, sizeof(s->block), 0, sizeof(s->block));
s->finalized = 1;
s->offset = s->rate;
}
static inline void
keccak_squeeze_blocks(keccak_state *s, uint8_t *out, size_t nr_blocks)
{
for (size_t n = 0; n < nr_blocks; n++) {
keccakf(s->a);
setout(s->a, out, s->rate);
out += s->rate;
}
}
static int
keccak_squeeze(keccak_state *s, uint8_t *out, size_t outlen)
{
if (!s->finalized)
return -1;
size_t remaining = outlen;
while (remaining > 0) {
if (s->offset == s->rate) {
const size_t blocks = remaining / s->rate;
const size_t direct_bytes = blocks * s->rate;
if (blocks > 0) {
keccak_squeeze_blocks(s, out, blocks);
out += direct_bytes;
remaining -= direct_bytes;
}
if (remaining > 0) {
keccak_squeeze_blocks(s, s->block, 1);
s->offset = 0;
}
}
const size_t buf_bytes = s->rate - s->offset;
const size_t indirect_bytes = (buf_bytes > remaining) ? remaining : buf_bytes;
if (indirect_bytes > 0) {
memcpy(out, &s->block[s->offset], indirect_bytes);
out += indirect_bytes;
s->offset += indirect_bytes;
remaining -= indirect_bytes;
}
}
return 0;
}
int
keccak_digest_init(keccak_state *s, size_t bits)
{
if (s == NULL)
return -1;
if (bits != 224 && bits != 256 && bits != 384 && bits != 512)
return -1;
keccak_cleanse(s);
s->rate = KECCAK_RATE(bits);
s->delim = KECCAK_DELIM_DIGEST;
return 0;
}
int
keccak_digest_update(keccak_state *s, const uint8_t *buf, size_t len)
{
if (s == NULL)
return -1;
if (s->delim != KECCAK_DELIM_DIGEST)
return -1;
return keccak_update(s, buf, len);
}
int
keccak_digest_sum(const keccak_state *s, uint8_t *out, size_t outlen)
{
if (s == NULL)
return -1;
if (s->delim != KECCAK_DELIM_DIGEST)
return -1;
if (out == NULL || outlen > 4 * (KECCAK_MAX_RATE - s->rate) / 8)
return -1;
// Work in a copy so that incremental/rolling hashes are easy.
keccak_state s_tmp;
keccak_clone(&s_tmp, s);
keccak_finalize(&s_tmp);
int ret = keccak_squeeze(&s_tmp, out, outlen);
keccak_cleanse(&s_tmp);
return ret;
}
int
keccak_xof_init(keccak_state *s, size_t bits)
{
if (s == NULL)
return -1;
if (bits != 128 && bits != 256)
return -1;
keccak_cleanse(s);
s->rate = KECCAK_RATE(bits);
s->delim = KECCAK_DELIM_XOF;
return 0;
}
int
keccak_xof_absorb(keccak_state *s, const uint8_t *buf, size_t len)
{
if (s == NULL)
return -1;
if (s->delim != KECCAK_DELIM_XOF)
return -1;
return keccak_update(s, buf, len);
}
int
keccak_xof_squeeze(keccak_state *s, uint8_t *out, size_t outlen)
{
if (s == NULL)
return -1;
if (s->delim != KECCAK_DELIM_XOF)
return -1;
if (!s->finalized)
keccak_finalize(s);
return keccak_squeeze(s, out, outlen);
}
void
keccak_clone(keccak_state *out, const keccak_state *in)
{
memcpy(out, in, sizeof(keccak_state));
}
void
keccak_cleanse(keccak_state *s)
{
memset_s(s, sizeof(keccak_state), 0, sizeof(keccak_state));
}
/** The sponge-based hash construction. **/
static inline int hash(uint8_t* out, size_t outlen,
const uint8_t* in, size_t inlen,
size_t rate, uint8_t delim) {
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
size_t bits, uint8_t delim) {
if ((out == NULL) || ((in == NULL) && inlen != 0)) {
return -1;
}
uint8_t a[Plen] = {0};
// Absorb input.
foldP(in, inlen, xorin);
// Xor in the DS and pad frame.
a[inlen] ^= delim;
a[rate - 1] ^= 0x80;
// Xor in the last block.
xorin(a, in, inlen);
// Apply P
P(a);
// Squeeze output.
foldP(out, outlen, setout);
setout(a, out, outlen);
memset_s(a, 200, 0, 200);
return 0;
int ret = 0;
keccak_state s;
switch (delim) {
case KECCAK_DELIM_DIGEST:
ret |= keccak_digest_init(&s, bits);
ret |= keccak_digest_update(&s, in, inlen);
// Use the internal API instead of sum to avoid the memcpy.
keccak_finalize(&s);
ret |= keccak_squeeze(&s, out, outlen);
break;
case KECCAK_DELIM_XOF:
ret |= keccak_xof_init(&s, bits);
ret |= keccak_xof_absorb(&s, in, inlen);
ret |= keccak_xof_squeeze(&s, out, outlen);
break;
default:
return -1;
}
keccak_cleanse(&s);
return ret;
}
/*** Helper macros to define SHA3 and SHAKE instances. ***/
#define defshake(bits) \
int shake##bits(uint8_t* out, size_t outlen, \
const uint8_t* in, size_t inlen) { \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \
return hash(out, outlen, in, inlen, bits, KECCAK_DELIM_XOF); \
}
#define defsha3(bits) \
int sha3_##bits(uint8_t* out, size_t outlen, \
@ -149,7 +356,7 @@ static inline int hash(uint8_t* out, size_t outlen,
if (outlen > (bits/8)) { \
return -1; \
} \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x06); \
return hash(out, outlen, in, inlen, bits, KECCAK_DELIM_DIGEST); \
}
/*** FIPS202 SHAKE VOFs ***/

View File

@ -4,6 +4,53 @@
#include <stdint.h>
#include <stdlib.h>
#define KECCAK_MAX_RATE 200
/* Calculate the rate (block size) from the security target. */
#define KECCAK_RATE(bits) (KECCAK_MAX_RATE - (bits / 4))
/* The internal structure of a FIPS202 hash/xof instance. Most callers
* should treat this as an opaque structure.
*/
typedef struct keccak_state {
uint8_t a[KECCAK_MAX_RATE];
size_t rate;
uint8_t delim;
uint8_t block[KECCAK_MAX_RATE];
size_t offset;
uint8_t finalized : 1;
} keccak_state;
/* Initialize a Keccak instance suitable for SHA-3 hash functions. */
int keccak_digest_init(keccak_state *s, size_t bits);
/* Feed more data into the SHA-3 hash instance. */
int keccak_digest_update(keccak_state *s, const uint8_t *buf, size_t len);
/* Calculate the SHA-3 hash digest. The state is unmodified to support
* calculating multiple/rolling digests.
*/
int keccak_digest_sum(const keccak_state *s, uint8_t *out, size_t outlen);
/* Initialize a Keccak instance suitable for XOFs (SHAKE-128/256). */
int keccak_xof_init(keccak_state *s, size_t bits);
/* Absorb more data into the XOF. Must not be called after a squeeze call. */
int keccak_xof_absorb(keccak_state *s, const uint8_t *buf, size_t len);
/* Squeeze data out of the XOF. Must not attempt to absorb additional data,
* after a squeeze has been called.
*/
int keccak_xof_squeeze(keccak_state *s, uint8_t *out, size_t outlen);
/* Clone an existing hash/XOF instance. */
void keccak_clone(keccak_state *out, const keccak_state *in);
/* Cleanse sensitive data from a given hash instance. */
void keccak_cleanse(keccak_state *s);
#define decshake(bits) \
int shake##bits(uint8_t*, size_t, const uint8_t*, size_t);