From b911fef216d1386210ec24e201d54d709528abb4 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Thu, 11 Apr 2024 10:29:23 +0200 Subject: [PATCH] Intentionally break EVP_DigestFinal for SHAKE128 and SHAKE256 It will work only if OSSL_DIGEST_PARAM_XOFLEN is set. Reviewed-by: Paul Dale Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/24105) --- crypto/evp/digest.c | 2 ++ crypto/sha/sha3.c | 4 +-- include/internal/sha3.h | 4 +-- providers/implementations/digests/sha3_prov.c | 19 ++++++++++---- test/evp_xof_test.c | 25 +++++++++++++++---- test/recipes/20-test_dgst.t | 8 +++--- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index ab670a8f49..4c61ca4c42 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -454,6 +454,8 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *isize) if (ctx->digest->prov == NULL) goto legacy; + if (sz == 0) /* Assuming a xoflen must have been set. */ + mdsize = SIZE_MAX; if (ctx->digest->gettable_ctx_params != NULL) { OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; diff --git a/crypto/sha/sha3.c b/crypto/sha/sha3.c index 2411b3f1f8..4d54712168 100644 --- a/crypto/sha/sha3.c +++ b/crypto/sha/sha3.c @@ -34,12 +34,12 @@ int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen) return 0; } -int ossl_keccak_kmac_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen) +int ossl_keccak_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen, size_t mdlen) { int ret = ossl_sha3_init(ctx, pad, bitlen); if (ret) - ctx->md_size *= 2; + ctx->md_size = mdlen / 8; return ret; } diff --git a/include/internal/sha3.h b/include/internal/sha3.h index 332916aa54..77bd9dbd32 100644 --- a/include/internal/sha3.h +++ b/include/internal/sha3.h @@ -51,8 +51,8 @@ struct keccak_st { void ossl_sha3_reset(KECCAK1600_CTX *ctx); int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen); -int ossl_keccak_kmac_init(KECCAK1600_CTX *ctx, unsigned char pad, - size_t bitlen); +int ossl_keccak_init(KECCAK1600_CTX *ctx, unsigned char pad, + size_t typelen, size_t mdlen); int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len); int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen); int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen); diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 2fd0f928e7..dce2461a9d 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -14,6 +14,7 @@ #include #include #include +#include "internal/numbers.h" #include "internal/sha3.h" #include "prov/digestcommon.h" #include "prov/implementations.h" @@ -112,6 +113,10 @@ static int keccak_final(void *vctx, unsigned char *out, size_t *outl, if (!ossl_prov_is_running()) return 0; + if (ctx->md_size == SIZE_MAX) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } if (outlen > 0) ret = ctx->meth.final(ctx, out, ctx->md_size); @@ -474,7 +479,7 @@ static void *name##_newctx(void *provctx) \ return ctx; \ } -#define SHAKE_newctx(typ, uname, name, bitlen, pad) \ +#define SHAKE_newctx(typ, uname, name, bitlen, mdlen, pad) \ static OSSL_FUNC_digest_newctx_fn name##_newctx; \ static void *name##_newctx(void *provctx) \ { \ @@ -483,7 +488,9 @@ static void *name##_newctx(void *provctx) \ \ if (ctx == NULL) \ return NULL; \ - ossl_sha3_init(ctx, pad, bitlen); \ + ossl_keccak_init(ctx, pad, bitlen, mdlen); \ + if (mdlen == 0) \ + ctx->md_size = SIZE_MAX; \ SHAKE_SET_MD(uname, typ) \ return ctx; \ } @@ -497,7 +504,7 @@ static void *uname##_newctx(void *provctx) \ \ if (ctx == NULL) \ return NULL; \ - ossl_keccak_kmac_init(ctx, pad, bitlen); \ + ossl_keccak_init(ctx, pad, bitlen, 2 * bitlen); \ KMAC_SET_MD(bitlen) \ return ctx; \ } @@ -585,10 +592,12 @@ static int shake_set_ctx_params(void *vctx, const OSSL_PARAM params[]) SHA3_FLAGS) #define IMPLEMENT_SHAKE_functions(bitlen) \ - SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, '\x1f') \ + SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, \ + 0 /* no default md length */, '\x1f') \ PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen, \ - SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \ + SHA3_BLOCKSIZE(bitlen), 0, \ SHAKE_FLAGS) + #define IMPLEMENT_KMAC_functions(bitlen) \ KMAC_newctx(keccak_kmac_##bitlen, bitlen, '\x04') \ PROV_FUNC_SHAKE_DIGEST(keccak_kmac_##bitlen, bitlen, \ diff --git a/test/evp_xof_test.c b/test/evp_xof_test.c index eeff8667c4..964005d0c1 100644 --- a/test/evp_xof_test.c +++ b/test/evp_xof_test.c @@ -206,14 +206,29 @@ static int shake_kat_digestfinal_test(void) EVP_MD_CTX *ctx = NULL; unsigned char out[sizeof(shake256_output)]; + /* Test that EVP_DigestFinal without setting XOFLEN fails */ if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) return 0; if (!TEST_true(EVP_DigestUpdate(ctx, shake256_input, - sizeof(shake256_input))) - || !TEST_true(EVP_DigestFinal(ctx, out, &digest_length)) - || !TEST_uint_eq(digest_length, 32) - || !TEST_mem_eq(out, digest_length, - shake256_output, digest_length) + sizeof(shake256_input)))) + return 0; + ERR_set_mark(); + if (!TEST_false(EVP_DigestFinal(ctx, out, &digest_length))) { + ERR_clear_last_mark(); + return 0; + } + ERR_pop_to_mark(); + EVP_MD_CTX_free(ctx); + + /* However EVP_DigestFinalXOF must work */ + if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) + return 0; + if (!TEST_true(EVP_DigestUpdate(ctx, shake256_input, + sizeof(shake256_input)))) + return 0; + if (!TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out))) + || !TEST_mem_eq(out, sizeof(out), + shake256_output, sizeof(shake256_output)) || !TEST_false(EVP_DigestFinalXOF(ctx, out, sizeof(out)))) goto err; ret = 1; diff --git a/test/recipes/20-test_dgst.t b/test/recipes/20-test_dgst.t index aed7bf3984..5f07789bfd 100644 --- a/test/recipes/20-test_dgst.t +++ b/test/recipes/20-test_dgst.t @@ -223,13 +223,11 @@ subtest "Custom length XOF digest generation with `dgst` CLI" => sub { }; subtest "SHAKE digest generation with no xoflen set `dgst` CLI" => sub { - plan tests => 1; + plan tests => 2; my $testdata = srctop_file('test', 'data.bin'); - my @xofdata = run(app(['openssl', 'dgst', '-shake128', $testdata], stderr => "outerr.txt"), capture => 1); - chomp(@xofdata); - my $expected = qr/SHAKE-128\(\Q$testdata\E\)= bb565dac72640109e1c926ef441d3fa6/; - ok($xofdata[0] =~ $expected, "Check short digest is output"); + ok(!run(app(['openssl', 'dgst', '-shake128', $testdata])), "SHAKE128 must fail without xoflen"); + ok(!run(app(['openssl', 'dgst', '-shake256', $testdata])), "SHAKE256 must fail without xoflen"); }; SKIP: {