mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 05:33:47 +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
@ -102,46 +102,253 @@ mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
|
|||||||
mkapply_sd(setout, dst[i] = src[i]) // setout
|
mkapply_sd(setout, dst[i] = src[i]) // setout
|
||||||
|
|
||||||
#define P keccakf
|
#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.
|
// Fold P*F over the full blocks of an input.
|
||||||
#define foldP(I, L, F) \
|
#define foldP(I, L, F) \
|
||||||
while (L >= rate) { \
|
while (L >= s->rate) { \
|
||||||
F(a, I, rate); \
|
F(s->a, I, s->rate); \
|
||||||
P(a); \
|
P(s->a); \
|
||||||
I += rate; \
|
I += s->rate; \
|
||||||
L -= 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. **/
|
/** The sponge-based hash construction. **/
|
||||||
static inline int hash(uint8_t* out, size_t outlen,
|
static inline int hash(uint8_t* out, size_t outlen,
|
||||||
const uint8_t* in, size_t inlen,
|
const uint8_t* in, size_t inlen,
|
||||||
size_t rate, uint8_t delim) {
|
size_t bits, uint8_t delim) {
|
||||||
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
|
if ((out == NULL) || ((in == NULL) && inlen != 0)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
uint8_t a[Plen] = {0};
|
|
||||||
// Absorb input.
|
int ret = 0;
|
||||||
foldP(in, inlen, xorin);
|
keccak_state s;
|
||||||
// Xor in the DS and pad frame.
|
|
||||||
a[inlen] ^= delim;
|
switch (delim) {
|
||||||
a[rate - 1] ^= 0x80;
|
case KECCAK_DELIM_DIGEST:
|
||||||
// Xor in the last block.
|
ret |= keccak_digest_init(&s, bits);
|
||||||
xorin(a, in, inlen);
|
ret |= keccak_digest_update(&s, in, inlen);
|
||||||
// Apply P
|
// Use the internal API instead of sum to avoid the memcpy.
|
||||||
P(a);
|
keccak_finalize(&s);
|
||||||
// Squeeze output.
|
ret |= keccak_squeeze(&s, out, outlen);
|
||||||
foldP(out, outlen, setout);
|
break;
|
||||||
setout(a, out, outlen);
|
case KECCAK_DELIM_XOF:
|
||||||
memset_s(a, 200, 0, 200);
|
ret |= keccak_xof_init(&s, bits);
|
||||||
return 0;
|
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. ***/
|
/*** Helper macros to define SHA3 and SHAKE instances. ***/
|
||||||
#define defshake(bits) \
|
#define defshake(bits) \
|
||||||
int shake##bits(uint8_t* out, size_t outlen, \
|
int shake##bits(uint8_t* out, size_t outlen, \
|
||||||
const uint8_t* in, size_t inlen) { \
|
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) \
|
#define defsha3(bits) \
|
||||||
int sha3_##bits(uint8_t* out, size_t outlen, \
|
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)) { \
|
if (outlen > (bits/8)) { \
|
||||||
return -1; \
|
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 ***/
|
/*** FIPS202 SHAKE VOFs ***/
|
||||||
|
@ -4,6 +4,53 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.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) \
|
#define decshake(bits) \
|
||||||
int shake##bits(uint8_t*, size_t, const uint8_t*, size_t);
|
int shake##bits(uint8_t*, size_t, const uint8_t*, size_t);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user