[PATCH] keccak: add md_read support for SHAKE algorithms
Jussi Kivilinna
jussi.kivilinna at iki.fi
Sun Jun 25 20:46:23 CEST 2023
* cipher/hash-common.c (_gcry_hash_selftest_check_one): Adjust
for SHAKE algorithms now returning non-zero for digest length.
* cipher/keccak.c (KECCAK_CONTEXT_S): Add 'shake_in_extract_mode'
and 'shake_in_read_mode' flags.
(keccak_init): Initialize new context fields; set output length
for SHAKE algorithms.
(keccak_extract): Rename to ...
(do_keccak_extract): this and add return value.
(keccak_extract): New with 'shake_in_???_mode' checks & setup.
(keccak_shake_read): New.
(_gcry_sha3_hash_buffers): Adjust for 'spec->mdlen' not being
zero for SHAKE algorithms, instead check 'suffix' for type.
(_gcry_digest_spec_shake128): Set mdlen to 32 bytes; Set read
function.
(_gcry_digest_spec_shake256): Set mdlen to 64 bytes; Set read
function.
* cipher/md.c (md_extract): Pass return value from algo extract
function.
(_gcry_md_hash_buffers_extract): Adjust for 'spec->mdlen' not
being zero for SHAKE algorithms.
* src/cipher-proto.h (gcry_md_extract_t): Change return type
from 'void' to 'gpg_err_code_t'.
* tests/basic.c (check_one_md, check_one_md_multi): Adjust
for 'gcry_md_get_algo_dlen()' not being zero for SHAKE
algorithms.
(check_digests): Add md_read interface test-vectors for SHAKE128
and SHAKE256.
--
GnuPG-bug-id: 6539
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
cipher/hash-common.c | 10 +++---
cipher/keccak.c | 76 +++++++++++++++++++++++++++++++++++++-------
cipher/md.c | 18 ++++++-----
src/cipher-proto.h | 3 +-
tests/basic.c | 44 +++++++++++++++----------
5 files changed, 108 insertions(+), 43 deletions(-)
diff --git a/cipher/hash-common.c b/cipher/hash-common.c
index ed2d7cac..deff40d7 100644
--- a/cipher/hash-common.c
+++ b/cipher/hash-common.c
@@ -51,12 +51,10 @@ _gcry_hash_selftest_check_one (int algo,
gcry_md_hd_t hd;
unsigned char *digest;
char aaa[1000];
- int xof = 0;
+ int expect_xof = 0;
- if (_gcry_md_get_algo_dlen (algo) == 0)
- xof = 1;
- else if (_gcry_md_get_algo_dlen (algo) != expectlen)
- return "digest size does not match expected size";
+ if (_gcry_md_get_algo_dlen (algo) != expectlen)
+ expect_xof = 1;
err = _gcry_md_open (&hd, algo, 0);
if (err)
@@ -85,7 +83,7 @@ _gcry_hash_selftest_check_one (int algo,
if (!result)
{
- if (!xof)
+ if (!expect_xof)
{
digest = _gcry_md_read (hd, algo);
diff --git a/cipher/keccak.c b/cipher/keccak.c
index 22c40302..9883af4d 100644
--- a/cipher/keccak.c
+++ b/cipher/keccak.c
@@ -143,7 +143,9 @@ typedef struct KECCAK_CONTEXT_S
unsigned int outlen;
unsigned int blocksize;
unsigned int count;
- unsigned int suffix;
+ unsigned int suffix:8;
+ unsigned int shake_in_extract_mode:1;
+ unsigned int shake_in_read_mode:1;
const keccak_ops_t *ops;
#ifdef USE_S390X_CRYPTO
unsigned int kimd_func;
@@ -966,6 +968,8 @@ keccak_init (int algo, void *context, unsigned int flags)
memset (hd, 0, sizeof *hd);
ctx->count = 0;
+ ctx->shake_in_extract_mode = 0;
+ ctx->shake_in_read_mode = 0;
/* Select generic implementation. */
#ifdef USE_64BIT
@@ -1024,12 +1028,12 @@ keccak_init (int algo, void *context, unsigned int flags)
case GCRY_MD_SHAKE128:
ctx->suffix = SHAKE_DELIMITED_SUFFIX;
ctx->blocksize = 1344 / 8;
- ctx->outlen = 0;
+ ctx->outlen = 256 / 8;
break;
case GCRY_MD_SHAKE256:
ctx->suffix = SHAKE_DELIMITED_SUFFIX;
ctx->blocksize = 1088 / 8;
- ctx->outlen = 0;
+ ctx->outlen = 512 / 8;
break;
default:
BUG();
@@ -1181,8 +1185,8 @@ keccak_read (void *context)
}
-static void
-keccak_extract (void *context, void *out, size_t outlen)
+static gcry_err_code_t
+do_keccak_extract (void *context, void *out, size_t outlen)
{
KECCAK_CONTEXT *ctx = context;
KECCAK_STATE *hd = &ctx->state;
@@ -1199,7 +1203,7 @@ keccak_extract (void *context, void *out, size_t outlen)
if (ctx->kimd_func)
{
keccak_extract_s390x (context, out, outlen);
- return;
+ return 0;
}
#endif
@@ -1304,6 +1308,52 @@ keccak_extract (void *context, void *out, size_t outlen)
if (burn)
_gcry_burn_stack (burn);
+
+ return 0;
+}
+
+
+static gcry_err_code_t
+keccak_extract (void *context, void *out, size_t outlen)
+{
+ KECCAK_CONTEXT *ctx = context;
+
+ if (ctx->shake_in_read_mode)
+ return GPG_ERR_INV_STATE;
+ if (!ctx->shake_in_extract_mode)
+ ctx->shake_in_extract_mode = 1;
+
+ return do_keccak_extract (context, out, outlen);
+}
+
+
+static byte *
+keccak_shake_read (void *context)
+{
+ KECCAK_CONTEXT *ctx = (KECCAK_CONTEXT *) context;
+ KECCAK_STATE *hd = &ctx->state;
+
+ if (ctx->shake_in_extract_mode)
+ {
+ /* Already in extract mode. */
+ return NULL;
+ }
+
+ if (!ctx->shake_in_read_mode)
+ {
+ byte tmpbuf[64];
+
+ gcry_assert(sizeof(tmpbuf) >= ctx->outlen);
+
+ ctx->shake_in_read_mode = 1;
+
+ do_keccak_extract (context, tmpbuf, ctx->outlen);
+ buf_cpy (&hd->u, tmpbuf, ctx->outlen);
+
+ wipememory(tmpbuf, sizeof(tmpbuf));
+ }
+
+ return (byte *)&hd->u;
}
@@ -1318,10 +1368,10 @@ _gcry_sha3_hash_buffers (void *outbuf, size_t nbytes, const gcry_buffer_t *iov,
for (;iovcnt > 0; iov++, iovcnt--)
keccak_write (&hd, (const char*)iov[0].data + iov[0].off, iov[0].len);
keccak_final (&hd);
- if (spec->mdlen > 0)
+ if (hd.suffix == SHA3_DELIMITED_SUFFIX)
memcpy (outbuf, keccak_read (&hd), spec->mdlen);
else
- keccak_extract (&hd, outbuf, nbytes);
+ do_keccak_extract (&hd, outbuf, nbytes);
}
@@ -1630,8 +1680,9 @@ const gcry_md_spec_t _gcry_digest_spec_sha3_512 =
const gcry_md_spec_t _gcry_digest_spec_shake128 =
{
GCRY_MD_SHAKE128, {0, 1},
- "SHAKE128", shake128_asn, DIM (shake128_asn), oid_spec_shake128, 0,
- shake128_init, keccak_write, keccak_final, NULL, keccak_extract,
+ "SHAKE128", shake128_asn, DIM (shake128_asn), oid_spec_shake128, 32,
+ shake128_init, keccak_write, keccak_final, keccak_shake_read,
+ keccak_extract,
_gcry_shake128_hash_buffers,
sizeof (KECCAK_CONTEXT),
run_selftests
@@ -1639,8 +1690,9 @@ const gcry_md_spec_t _gcry_digest_spec_shake128 =
const gcry_md_spec_t _gcry_digest_spec_shake256 =
{
GCRY_MD_SHAKE256, {0, 1},
- "SHAKE256", shake256_asn, DIM (shake256_asn), oid_spec_shake256, 0,
- shake256_init, keccak_write, keccak_final, NULL, keccak_extract,
+ "SHAKE256", shake256_asn, DIM (shake256_asn), oid_spec_shake256, 64,
+ shake256_init, keccak_write, keccak_final, keccak_shake_read,
+ keccak_extract,
_gcry_shake256_hash_buffers,
sizeof (KECCAK_CONTEXT),
run_selftests
diff --git a/cipher/md.c b/cipher/md.c
index 40a862f6..a128dd82 100644
--- a/cipher/md.c
+++ b/cipher/md.c
@@ -1125,8 +1125,8 @@ md_extract(gcry_md_hd_t a, int algo, void *out, size_t outlen)
{
if (r->next)
log_debug ("more than one algorithm in md_extract(0)\n");
- r->spec->extract (r->context, out, outlen);
- return 0;
+
+ return r->spec->extract (r->context, out, outlen);
}
}
else
@@ -1134,8 +1134,7 @@ md_extract(gcry_md_hd_t a, int algo, void *out, size_t outlen)
for (r = a->ctx->list; r; r = r->next)
if (r->spec->algo == algo && r->spec->extract)
{
- r->spec->extract (r->context, out, outlen);
- return 0;
+ return r->spec->extract (r->context, out, outlen);
}
}
@@ -1248,6 +1247,7 @@ _gcry_md_hash_buffers_extract (int algo, unsigned int flags, void *digest,
int iovcnt)
{
const gcry_md_spec_t *spec;
+ int is_xof;
int hmac;
if (!iov || iovcnt < 0)
@@ -1266,11 +1266,13 @@ _gcry_md_hash_buffers_extract (int algo, unsigned int flags, void *digest,
return GPG_ERR_DIGEST_ALGO;
}
- if (spec->mdlen > 0 && digestlen != -1 && digestlen != spec->mdlen)
- return GPG_ERR_DIGEST_ALGO;
- if (spec->mdlen == 0 && digestlen == -1)
+ is_xof = spec->extract != NULL;
+ if (!is_xof && digestlen != -1 && digestlen != spec->mdlen)
return GPG_ERR_DIGEST_ALGO;
+ if (digestlen == -1)
+ digestlen = spec->mdlen;
+
if (!hmac && spec->hash_buffers)
{
if (spec->flags.disabled || (!spec->flags.fips && fips_mode ()))
@@ -1304,7 +1306,7 @@ _gcry_md_hash_buffers_extract (int algo, unsigned int flags, void *digest,
for (;iovcnt; iov++, iovcnt--)
md_write (h, (const char*)iov[0].data + iov[0].off, iov[0].len);
md_final (h);
- if (spec->mdlen > 0)
+ if (digestlen == spec->mdlen)
memcpy (digest, md_read (h, algo), spec->mdlen);
else if (digestlen > 0)
md_extract (h, algo, digest, digestlen);
diff --git a/src/cipher-proto.h b/src/cipher-proto.h
index 3cf6f74c..2d03ed7d 100644
--- a/src/cipher-proto.h
+++ b/src/cipher-proto.h
@@ -216,7 +216,8 @@ typedef void (*gcry_md_final_t) (void *c);
typedef unsigned char *(*gcry_md_read_t) (void *c);
/* Type for the md_extract function. */
-typedef void (*gcry_md_extract_t) (void *c, void *outbuf, size_t nbytes);
+typedef gpg_err_code_t (*gcry_md_extract_t) (void *c, void *outbuf,
+ size_t nbytes);
/* Type for the md_hash_buffers function. */
typedef void (*gcry_md_hash_buffers_t) (void *outbuf, size_t nbytes,
diff --git a/tests/basic.c b/tests/basic.c
index 5d5ceac9..a405c82c 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -13604,18 +13604,16 @@ check_one_md (int algo, const char *data, int len, const char *expect, int elen,
}
mdlen = gcry_md_get_algo_dlen (algo);
- if (mdlen < 1 || mdlen > 500)
+ if (elen != 0 && mdlen != elen
+ && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
{
- if (mdlen == 0 && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
- {
- xof = 1;
- }
- else
- {
- gcry_md_close (hd);
- fail ("algo %d, gcry_md_get_algo_dlen failed: %d\n", algo, mdlen);
- return;
- }
+ xof = 1;
+ }
+ else if (mdlen < 1 || mdlen > 500)
+ {
+ gcry_md_close (hd);
+ fail ("algo %d, gcry_md_get_algo_dlen failed: %d\n", algo, mdlen);
+ return;
}
if (key && klen)
@@ -13942,7 +13940,8 @@ check_one_md (int algo, const char *data, int len, const char *expect, int elen,
static void
-check_one_md_multi (int algo, const char *data, int len, const char *expect)
+check_one_md_multi (int algo, const char *data, int len, const char *expect,
+ int elen)
{
gpg_error_t err;
gcry_buffer_t iov[3];
@@ -13954,14 +13953,15 @@ check_one_md_multi (int algo, const char *data, int len, const char *expect)
mdlen = gcry_md_get_algo_dlen (algo);
if (mdlen < 1 || mdlen > 64)
{
- if (mdlen == 0 && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
- return;
-
fail ("check_one_md_multi: algo %d, gcry_md_get_algo_dlen failed: %d\n",
algo, mdlen);
return;
}
+ if (elen != 0 && elen != mdlen
+ && (algo == GCRY_MD_SHAKE128 || algo == GCRY_MD_SHAKE256))
+ return;
+
if (*data == '!' && !data[1])
return; /* We can't do that here. */
if (*data == '?' && !data[1])
@@ -14716,6 +14716,11 @@ check_digests (void)
"\x43\xE4\x1B\x45\xA6\x53\xF2\xA5\xC4\x49\x2C\x1A\xDD\x54\x45\x12"
"\xDD\xA2\x52\x98\x33\x46\x2B\x71\xA4\x1A\x45\xBE\x97\x29\x0B\x6F",
0, 512, },
+ { GCRY_MD_SHAKE128,
+ "",
+ "\x7F\x9C\x2B\xA4\xE8\x8F\x82\x7D\x61\x60\x45\x50\x76\x05\x85\x3E"
+ "\xD7\x3B\x80\x93\xF6\xEF\xBC\x88\xEB\x1A\x6E\xAC\xFA\x66\xEF\x26",
+ 0, 0, /* test md_read interface */ },
{ GCRY_MD_SHAKE128,
"\x5A\xAB\x62\x75\x6D\x30\x7A\x66\x9D\x14\x6A\xBA\x98\x8D\x90\x74"
"\xC5\xA1\x59\xB3\xDE\x85\x15\x1A\x81\x9B\x11\x7C\xA1\xFF\x65\x97"
@@ -14834,6 +14839,13 @@ check_digests (void)
"\xAB\x0B\xAE\x31\x63\x39\x89\x43\x04\xE3\x58\x77\xB0\xC2\x8A\x9B"
"\x1F\xD1\x66\xC7\x96\xB9\xCC\x25\x8A\x06\x4A\x8F\x57\xE2\x7F\x2A",
0, 512, },
+ { GCRY_MD_SHAKE256,
+ "",
+ "\x46\xB9\xDD\x2B\x0B\xA8\x8D\x13\x23\x3B\x3F\xEB\x74\x3E\xEB\x24"
+ "\x3F\xCD\x52\xEA\x62\xB8\x1B\x82\xB5\x0C\x27\x64\x6E\xD5\x76\x2F"
+ "\xD7\x5D\xC4\xDD\xD8\xC0\xF2\x00\xCB\x05\x01\x9D\x67\xB5\x92\xF6"
+ "\xFC\x82\x1C\x49\x47\x9A\xB4\x86\x40\x29\x2E\xAC\xB3\xB7\xC4\xBE",
+ 0, 0, /* test md_read interface */ },
{ GCRY_MD_SHAKE256,
"\xB3\x2D\x95\xB0\xB9\xAA\xD2\xA8\x81\x6D\xE6\xD0\x6D\x1F\x86\x00"
"\x85\x05\xBD\x8C\x14\x12\x4F\x6E\x9A\x16\x3B\x5A\x2A\xDE\x55\xF8"
@@ -15416,7 +15428,7 @@ check_digests (void)
check_one_md_multi (algos[i].md, algos[i].data,
algos[i].datalen > 0 ? algos[i].datalen
: strlen (algos[i].data),
- algos[i].expect);
+ algos[i].expect, algos[i].expectlen);
}
/* Check the Whirlpool bug emulation. */
--
2.39.2
More information about the Gcrypt-devel
mailing list