mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-24 14:51:11 +01:00
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:
parent
e993003792
commit
18685df031
2 changed files with 279 additions and 25 deletions
|
@ -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 ***/
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue