|
@@ -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; \
|
|
|
}
|
|
|
|
|
|
-/** 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)) {
|
|
|
+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;
|
|
|
+ }
|
|
|
}
|
|
|
- uint8_t a[Plen] = {0};
|
|
|
- // Absorb input.
|
|
|
- foldP(in, inlen, xorin);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+keccak_finalize(keccak_state *s)
|
|
|
+{
|
|
|
// Xor in the DS and pad frame.
|
|
|
- a[inlen] ^= delim;
|
|
|
- a[rate - 1] ^= 0x80;
|
|
|
+ s->a[s->offset] ^= s->delim;
|
|
|
+ s->a[s->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);
|
|
|
+ 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 bits, uint8_t delim) {
|
|
|
+ if ((out == NULL) || ((in == NULL) && inlen != 0)) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 ***/
|