From 9660ac3f099738a4896fb432dd0a6fda333fa511 Mon Sep 17 00:00:00 2001
From: manastasova <44320407+manastasova@users.noreply.github.com>
Date: Wed, 5 Feb 2025 11:57:36 -0800
Subject: [PATCH] SHA3 and SHAKE - New API Design (#2098)
### Issues:
Resolves #CryptoAlg-2810
### Description of changes:
AWS-LC supports SHA3 and SHAKE algorithms though low level SHA3_Init,
SHA3_Update, SHA3_Final and SHAKE_init, SHAKE_Final APIs. Currently,
there are two issues with the implementation and usage of SHA3 and
SHAKE:
- There is no support for SHAKE_Update function. SHAKE is implemented by
calling SHAKE_Init, SHA3_Update and SHAKE_Final.
- SHAKE_Final allows multiple consecutive calls to enable incremental
XOF output generation.
This PR addresses both of them as follows:
- Introduce new API layers - FIPS202, SHA3 and SHAKE.
- _Keccak1600_ layer (https://github.com/aws/aws-lc/pull/2097)
implements KeccakF1600 Absorb and Squeeze functions; Keccak1600 layer
does _not_ manage internal input/output buffers.
- _FIPS202_ layer implements Reset, Init, Update, and Finalize
functionalities; FIPS202 layer manages the internal input/output
buffers, allowing incremental requests (not necessarily multiple of
block size) to Update (Absorb) and Squeeze for input/output processing.
(Other functionalities, such as zero-ing of bitstate, block size checks,
etc. are also handled by FIPS202 API layer).
- _FIPS202_ layer implements all common behavior between SHA3 and SHAKE
algorithms.
- _FIPS202_ layer checks/updates the |ctx->state| flag when handling a
common behavior between SHA3 and SHAKE algorithms. |ctx->state| is
updated in the higher level SHA3_ SHAKE_ API layer when the behavior of
both algorithms diverges (SHAKE _can_ allow incremental squeezes).
- _SHA3_ layer implements Init, Update, and Final functionalities; SHA3
layer only implements SHA3 algorithm, thus, offers a single-call
SHA3_Final function. SHA3_Final will update internal |ctx->state| flag
to prevent any sequential calls.
- _SHAKE_ layer implements XOF SHAKE algorithm, therefore, offers Init,
Absorb, Squeeze, and Final functionalities;
- _SHAKE_ layer implements Init, and Absorb, Squeeze with incremental
call support for absorb (byte-wise) and squeeze (block-wise).
- _SHAKE_ layer implements a single-call SHAKE_Final function that
generates an arbitrary length output and finalizes SHAKE. Incremental
XOF output generation is handled by |SHAKE_Squeeze|. |SHAKE_Squeeze| can
be called multiple times. SHAKE_Final should be called only once.
- KECCAK600_CTX struct updates:
- Remove |padded| field
- Introduce |state| field
- |state| can be |KECCAK1600_STATE_ABSORB|, |KECCAK1600_STATE_SQUEEZE|,
|KECCAK1600_STATE_FINAL|
- |KECCAK1600_STATE_ABSORB| - allows incremental absorbs until the state
is changed
- |KECCAK1600_STATE_SQUEEZE| - allows incremental squeezes for
|SHAKE_Squeeze|
- |KECCAK1600_STATE_Final| - prevents from incremental squeezes via
|SHAKE_Final| and prevents from consecutive calls to |SHA3_Final| (Final
functions are single-shot functions).
SHA3 vs SHAKE algorithms (APIs usage):
>- SHA3 digest generation: SHA3_Init; SHA3_Update; SHA3_Final;
>- SHAKE (single-shot-output) output generation: SHAKE_Init;
SHAKE_Absorb; SHAKE_Final;
>- SHAKE (incremental) output generation: SHAKE_Init; SHAKE_Absorb;
SHAKE_Squeeze+;
### Call-outs:
Service indicator is updated:
- Inside SHA3 and SHAKE single shot APIs (as previously in AWS-LC);
- Inside SHA3_Final (as previously in AWS-LC);
- Inside SHAKE_Final (Single-Shot XOF Final output generation as
previously in AWS-LC);
- Inside SHAKE_Squeeze (Streaming XOF Squeezes output generation updates
the service indicator after each extendable output update);
All other algorithms that use SHA3/SHAKE APIs are updated:
- ML-KEM (SHA3/SHAKE calls will be inlined later)
- ML-DSA (SHAKE_Squeeze (incremental XOF output functionality) inside
ML-DSA is never invoked with the KAT test vectors and gtests)
### Testing:
_./crypto/crypto_test --gtest_filter="KeccakInternalTest.*"_
_./crypto/crypto_test --gtest_filter="SHA3Test.*"_
_./crypto/crypto_test --gtest_filter="SHAKETest.*"_
_./crypto/crypto_test --gtest_filter="All/PerKEMTest.*"_
_./crypto/crypto_test --gtest_filter="All/PQDSAParameterTest.*"_
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.
---------
Co-authored-by: Jake Massimo
Co-authored-by: Will Childs-Klein
Co-authored-by: Justin W Smith <103147162+justsmth@users.noreply.github.com>
Co-authored-by: Shubham Mittal <107728331+smittals2@users.noreply.github.com>
Co-authored-by: Samuel Chiang
Co-authored-by: David Benjamin
Co-authored-by: Theo Buehler
Co-authored-by: Adam Langley
Co-authored-by: Brian Ledger
Co-authored-by: Nick Harper
Co-authored-by: Andrew Hopkins
Co-authored-by: torben-hansen <50673096+torben-hansen@users.noreply.github.com>
Co-authored-by: Sean McGrail <549813+skmcgrail@users.noreply.github.com>
Co-authored-by: olivergillespie <62296743+olivergillespie@users.noreply.github.com>
---
crypto/fipsmodule/digest/digests.c | 4 +-
.../ml_kem/ml_kem_ref/symmetric-shake.c | 20 +-
crypto/fipsmodule/sha/internal.h | 61 +++--
crypto/fipsmodule/sha/sha3.c | 214 +++++++++++++-----
crypto/fipsmodule/sha/sha3_test.cc | 1 -
crypto/ml_dsa/ml_dsa_ref/poly.c | 28 +--
crypto/ml_dsa/ml_dsa_ref/sign.c | 15 +-
7 files changed, 236 insertions(+), 107 deletions(-)
diff --git a/crypto/fipsmodule/digest/digests.c b/crypto/fipsmodule/digest/digests.c
index 88b45d2e5f..d869723685 100644
--- a/crypto/fipsmodule/digest/digests.c
+++ b/crypto/fipsmodule/digest/digests.c
@@ -430,7 +430,7 @@ static void shake128_init(EVP_MD_CTX *ctx) {
}
static void shake128_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
- CHECK(SHA3_Update(ctx->md_data, data, count));
+ CHECK(SHAKE_Absorb(ctx->md_data, data, count));
}
static void shake128_final(EVP_MD_CTX *ctx, uint8_t *md, size_t len) {
@@ -455,7 +455,7 @@ static void shake256_init(EVP_MD_CTX *ctx) {
}
static void shake256_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
- CHECK(SHA3_Update(ctx->md_data, data, count));
+ CHECK(SHAKE_Absorb(ctx->md_data, data, count));
}
static void shake256_finalXOF(EVP_MD_CTX *ctx, uint8_t *md, size_t len) {
diff --git a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c
index d31d7136fd..72bf98c2f2 100644
--- a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c
+++ b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c
@@ -29,8 +29,8 @@ void kyber_shake128_absorb(KECCAK1600_CTX *ctx,
// SHAKE_Init always returns 1 when called with correct block size value
SHAKE_Init(ctx, SHAKE128_BLOCKSIZE);
- // SHA3_Update always returns 1 on first call of sizeof(extseed) (34 bytes)
- SHA3_Update(ctx, extseed, sizeof(extseed));
+ // SHAKE_Absorb always returns 1 on first call of sizeof(extseed) (34 bytes)
+ SHAKE_Absorb(ctx, extseed, sizeof(extseed));
}
/*************************************************
@@ -48,8 +48,9 @@ void kyber_shake128_absorb(KECCAK1600_CTX *ctx,
void kyber_shake128_squeeze(KECCAK1600_CTX *ctx, uint8_t *out, int nblocks)
{
// Return code checks can be omitted
- // SHAKE_Final always returns 1
- SHAKE_Final(out, ctx, nblocks * SHAKE128_BLOCKSIZE);
+ // SHAKE_Squeeze always returns 1 when |ctx->state| flag is different
+ // from |KECCAK1600_STATE_FINAL|
+ SHAKE_Squeeze(out, ctx, nblocks * SHAKE128_BLOCKSIZE);
}
/*************************************************
@@ -94,12 +95,13 @@ void kyber_shake256_rkprf(ml_kem_params *params, uint8_t out[KYBER_SSBYTES], con
// SHAKE_Init always returns 1 when called with correct block size value
SHAKE_Init(&ctx, SHAKE256_BLOCKSIZE);
- // SHA3_Update always returns 1 on first call of KYBER_SYMBYTES (32 bytes)
- SHA3_Update(&ctx, key, KYBER_SYMBYTES);
+ // SHAKE_Absorb always returns 1 on first call of KYBER_SYMBYTES (32 bytes)
+ SHAKE_Absorb(&ctx, key, KYBER_SYMBYTES);
- // SHA3_Update always returns 1 processing all data blocks that don't need pad
- SHA3_Update(&ctx, input, params->ciphertext_bytes);
+ // SHAKE_Absorb always returns 1 processing all data blocks that don't need pad
+ SHAKE_Absorb(&ctx, input, params->ciphertext_bytes);
- // SHAKE_Final always returns 1
+ // SHAKE_Final always returns 1 when |ctx->state| flag is set to
+ // |KECCAK1600_STATE_ABSORB| (no previous calls to SHAKE_Final)
SHAKE_Final(out, &ctx, KYBER_SSBYTES);
}
diff --git a/crypto/fipsmodule/sha/internal.h b/crypto/fipsmodule/sha/internal.h
index 3a0a1c37ce..73d389cb61 100644
--- a/crypto/fipsmodule/sha/internal.h
+++ b/crypto/fipsmodule/sha/internal.h
@@ -71,6 +71,16 @@ extern "C" {
// SHAKE128 has the maximum block size among the SHA3/SHAKE algorithms.
#define SHA3_MAX_BLOCKSIZE SHAKE128_BLOCKSIZE
+// Define state flag values for Keccak-based functions
+#define KECCAK1600_STATE_ABSORB 0
+// KECCAK1600_STATE_SQUEEZE is set when |SHAKE_Squeeze| is called.
+// It remains set while |SHAKE_Squeeze| is called repeatedly to output
+// chunks of the XOF output.
+#define KECCAK1600_STATE_SQUEEZE 1
+// KECCAK1600_STATE_FINAL is set once |SHAKE_Final| is called
+// so that |SHAKE_Squeeze| cannot be called anymore.
+#define KECCAK1600_STATE_FINAL 2
+
typedef struct keccak_st KECCAK1600_CTX;
// The data buffer should have at least the maximum number of
@@ -82,7 +92,7 @@ struct keccak_st {
size_t buf_load; // used bytes in below buffer
uint8_t buf[SHA3_MAX_BLOCKSIZE]; // should have at least the max data block size bytes
uint8_t pad; // padding character
- uint8_t padded; // denotes if padding has been performed
+ uint8_t state; // denotes the keccak phase (absorb, squeeze, final)
};
// Define SHA{n}[_{variant}]_ASM if sha{n}_block_data_order[_{variant}] is
@@ -396,32 +406,43 @@ OPENSSL_EXPORT uint8_t *SHAKE128(const uint8_t *data, const size_t in_len,
OPENSSL_EXPORT uint8_t *SHAKE256(const uint8_t *data, const size_t in_len,
uint8_t *out, size_t out_len);
-// SHAKE_Init initializes |ctx| with specified |block_size|, returns 1 on
-// success and 0 on failure. Calls SHA3_Init under the hood.
-int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size);
-
-// SHAKE_Final writes |len| bytes of finalized digest to |md|, returns 1 on
-// success and 0 on failure. Calls SHA3_Final under the hood.
-int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len);
-
-// SHA3_Reset zeros the bitstate and the amount of processed input.
-void SHA3_Reset(KECCAK1600_CTX *ctx);
-
-// SHA3_Init initialises |ctx| fields and returns 1 on success and 0 on failure.
+// SHA3_Init initialises |ctx| fields through |FIPS202_Init| and
+// returns 1 on success and 0 on failure.
OPENSSL_EXPORT int SHA3_Init(KECCAK1600_CTX *ctx, size_t bitlen);
-// SHA3_Update processes all data blocks that don't need pad through
-// |Keccak1600_Absorb| and returns 1 and 0 on failure.
+ // SHA3_Update check |ctx| pointer and |len| value, calls |FIPS202_Update|
+ // and returns 1 on success and 0 on failure.
int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len);
-// SHA3_Final pads the last data block and processes it through |Keccak1600_Absorb|.
-// It processes the data through |Keccak1600_Squeeze| and returns 1 and 0 on failure.
+// SHA3_Final pads the last data block and absorbs it through |FIPS202_Finalize|.
+// It then calls |Keccak1600_Squeeze| and returns 1 on success
+// and 0 on failure.
int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx);
-// Keccak1600_Absorb processes the largest multiple of |r| out of |len| bytes and
-// returns the remaining number of bytes.
+// SHAKE_Init initialises |ctx| fields through |FIPS202_Init| and
+// returns 1 on success and 0 on failure.
+int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size);
+
+// SHAKE_Absorb checks |ctx| pointer and |len| values. It updates and absorbs
+// input blocks via |FIPS202_Update|.
+int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data,
+ size_t len);
+
+// SHAKE_Squeeze pads the last data block and absorbs it through
+// |FIPS202_Finalize| on first call. It writes |len| bytes of incremental
+// XOF output to |md| and returns 1 on success and 0 on failure. It can be
+// called multiple times.
+int SHAKE_Squeeze(uint8_t *md, KECCAK1600_CTX *ctx, size_t len);
+
+// SHAKE_Final writes |len| bytes of finalized extendible output to |md|, returns 1 on
+// success and 0 on failure. It should be called once to finalize absorb and
+// squeeze phases. Incremental XOF output should be generated via |SHAKE_Squeeze|.
+int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len);
+
+// Keccak1600_Absorb processes the largest multiple of |r| (block size) out of
+// |len| bytes and returns the remaining number of bytes.
size_t Keccak1600_Absorb(uint64_t A[KECCAK1600_ROWS][KECCAK1600_ROWS],
- const uint8_t *data, size_t len, size_t r);
+ const uint8_t *data, size_t len, size_t r);
// Keccak1600_Squeeze generates |out| value of |len| bytes (per call). It can be called
// multiple times when used as eXtendable Output Function. |padded| indicates
diff --git a/crypto/fipsmodule/sha/sha3.c b/crypto/fipsmodule/sha/sha3.c
index 708d554a6d..16b098023a 100644
--- a/crypto/fipsmodule/sha/sha3.c
+++ b/crypto/fipsmodule/sha/sha3.c
@@ -82,7 +82,7 @@ uint8_t *SHAKE128(const uint8_t *data, const size_t in_len, uint8_t *out, size_t
FIPS_service_indicator_lock_state();
KECCAK1600_CTX ctx;
int ok = (SHAKE_Init(&ctx, SHAKE128_BLOCKSIZE) &&
- SHA3_Update(&ctx, data, in_len) &&
+ SHAKE_Absorb(&ctx, data, in_len) &&
SHAKE_Final(out, &ctx, out_len));
OPENSSL_cleanse(&ctx, sizeof(ctx));
@@ -98,7 +98,7 @@ uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t
FIPS_service_indicator_lock_state();
KECCAK1600_CTX ctx;
int ok = (SHAKE_Init(&ctx, SHAKE256_BLOCKSIZE) &&
- SHA3_Update(&ctx, data, in_len) &&
+ SHAKE_Absorb(&ctx, data, in_len) &&
SHAKE_Final(out, &ctx, out_len));
OPENSSL_cleanse(&ctx, sizeof(ctx));
FIPS_service_indicator_unlock_state();
@@ -111,9 +111,9 @@ uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t
// FIPS202 APIs manage internal input/output buffer on top of Keccak1600 API layer
static void FIPS202_Reset(KECCAK1600_CTX *ctx) {
- memset(ctx->A, 0, sizeof(ctx->A));
+ OPENSSL_memset(ctx->A, 0, sizeof(ctx->A));
ctx->buf_load = 0;
- ctx->padded=0;
+ ctx->state = KECCAK1600_STATE_ABSORB;
}
static int FIPS202_Init(KECCAK1600_CTX *ctx, uint8_t pad, size_t block_size, size_t bit_len) {
@@ -132,47 +132,31 @@ static int FIPS202_Init(KECCAK1600_CTX *ctx, uint8_t pad, size_t block_size, siz
return 0;
}
-// SHA3 APIs implement SHA3 functionalities on top of FIPS202 API layer
-void SHA3_Reset(KECCAK1600_CTX *ctx) {
- memset(ctx->A, 0, sizeof(ctx->A));
- ctx->buf_load = 0;
- ctx->padded = 0;
-}
-
-int SHA3_Init(KECCAK1600_CTX *ctx, size_t bit_len) {
- if (bit_len == SHA3_224_DIGEST_BITLENGTH ||
- bit_len == SHA3_256_DIGEST_BITLENGTH ||
- bit_len == SHA3_384_DIGEST_BITLENGTH ||
- bit_len == SHA3_512_DIGEST_BITLENGTH) {
- // |block_size| depends on the SHA3 |bit_len| output (digest) length
- return FIPS202_Init(ctx, SHA3_PAD_CHAR, SHA3_BLOCKSIZE(bit_len), bit_len);
- }
- return 0;
-}
-
-int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) {
+static int FIPS202_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) {
uint8_t *data_ptr_copy = (uint8_t *) data;
size_t block_size = ctx->block_size;
size_t num, rem;
- if (len == 0) {
- return 1;
+ if (ctx->state == KECCAK1600_STATE_SQUEEZE ||
+ ctx->state == KECCAK1600_STATE_FINAL ) {
+ return 0;
}
+ // Case |len| equals 0 is checked in SHA3/SHAKE higher level APIs
// Process intermediate buffer.
num = ctx->buf_load;
if (num != 0) {
rem = block_size - num;
if (len < rem) {
- memcpy(ctx->buf + num, data_ptr_copy, len);
+ OPENSSL_memcpy(ctx->buf + num, data_ptr_copy, len);
ctx->buf_load += len;
return 1;
}
- // There is enough data to fill or overflow the intermediate
- // buffer. So we append |rem| bytes and process the block,
- // leaving the rest for later processing.
- memcpy(ctx->buf + num, data_ptr_copy, rem);
+ // There is enough data to fill or overflow the intermediate
+ // buffer. So we append |rem| bytes and process the block,
+ // leaving the rest for later processing.
+ OPENSSL_memcpy(ctx->buf + num, data_ptr_copy, rem);
data_ptr_copy += rem, len -= rem;
if (Keccak1600_Absorb(ctx->A, ctx->buf, block_size, block_size) != 0 ) {
return 0;
@@ -189,54 +173,176 @@ int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) {
}
if (rem != 0) {
- memcpy(ctx->buf, data_ptr_copy + len - rem, rem);
+ OPENSSL_memcpy(ctx->buf, data_ptr_copy + len - rem, rem);
ctx->buf_load = rem;
}
return 1;
}
-int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) {
+// FIPS202_Finalize processes padding and absorb of last input block
+// This function should be called once to finalize absorb and initiate squeeze phase
+static int FIPS202_Finalize(uint8_t *md, KECCAK1600_CTX *ctx) {
size_t block_size = ctx->block_size;
size_t num = ctx->buf_load;
- if (ctx->md_size == 0) {
+ if (ctx->state == KECCAK1600_STATE_SQUEEZE ||
+ ctx->state == KECCAK1600_STATE_FINAL ) {
+ return 0;
+ }
+
+ // Pad the data with 10*1. Note that |num| can be |block_size - 1|
+ // in which case both byte operations below are performed on
+ // the same byte.
+ OPENSSL_memset(ctx->buf + num, 0, block_size - num);
+ ctx->buf[num] = ctx->pad;
+ ctx->buf[block_size - 1] |= 0x80;
+
+ if (Keccak1600_Absorb(ctx->A, ctx->buf, block_size, block_size) != 0) {
+ return 0;
+ }
+
+ // ctx->buf is processed, ctx->buf_load is guaranteed to be zero
+ ctx->buf_load = 0;
+
+ return 1;
+}
+
+// SHA3 APIs implement SHA3 functionalities on top of FIPS202 API layer
+int SHA3_Init(KECCAK1600_CTX *ctx, size_t bit_len) {
+ if (ctx == NULL) {
+ return 0;
+ }
+
+ if (bit_len != SHA3_224_DIGEST_BITLENGTH &&
+ bit_len != SHA3_256_DIGEST_BITLENGTH &&
+ bit_len != SHA3_384_DIGEST_BITLENGTH &&
+ bit_len != SHA3_512_DIGEST_BITLENGTH) {
+ return 0;
+ }
+ // |block_size| depends on the SHA3 |bit_len| output (digest) length
+ return FIPS202_Init(ctx, SHA3_PAD_CHAR, SHA3_BLOCKSIZE(bit_len), bit_len);
+}
+
+int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) {
+ if (ctx == NULL) {
+ return 0;
+ }
+
+ if (data == NULL && len != 0) {
+ return 0;
+ }
+
+ if (len == 0) {
return 1;
}
- if (ctx->padded == 0) {
- // Pad the data with 10*1. Note that |num| can be |block_size - 1|
- // in which case both byte operations below are performed on
- // the same byte.
- memset(ctx->buf + num, 0, block_size - num);
- ctx->buf[num] = ctx->pad;
- ctx->buf[block_size - 1] |= 0x80;
+ return FIPS202_Update(ctx, data, len);
+}
- if (Keccak1600_Absorb(ctx->A, ctx->buf, block_size, block_size) != 0) {
- return 0;
- }
+// SHA3_Final should be called once to process final digest value
+int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) {
+ if (md == NULL || ctx == NULL) {
+ return 0;
}
- Keccak1600_Squeeze(ctx->A, md, ctx->md_size, block_size, ctx->padded);
- ctx->padded = 1;
+ if (ctx->md_size == 0) {
+ return 1;
+ }
- FIPS_service_indicator_update_state();
+ if (FIPS202_Finalize(md, ctx) == 0) {
+ return 0;
+ }
+ Keccak1600_Squeeze(ctx->A, md, ctx->md_size, ctx->block_size, ctx->state);
+ ctx->state = KECCAK1600_STATE_FINAL;
+
+ FIPS_service_indicator_update_state();
return 1;
}
-// SHAKE APIs implement SHAKE functionalities on top of FIPS202 API layer
int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size) {
- if (block_size == SHAKE128_BLOCKSIZE ||
- block_size == SHAKE256_BLOCKSIZE) {
- // |block_size| depends on the SHAKE security level
- // The output length |bit_len| is initialized to 0
- return FIPS202_Init(ctx, SHAKE_PAD_CHAR, block_size, 0);
+ if (ctx == NULL) {
+ return 0;
+ }
+
+ if (block_size != SHAKE128_BLOCKSIZE &&
+ block_size != SHAKE256_BLOCKSIZE) {
+ return 0;
+ }
+ // |block_size| depends on the SHAKE security level
+ // The output length |bit_len| is initialized to 0
+ return FIPS202_Init(ctx, SHAKE_PAD_CHAR, block_size, 0);
+}
+
+int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, size_t len) {
+ if (ctx == NULL) {
+ return 0;
+ }
+
+ if (data == NULL && len != 0) {
+ return 0;
+ }
+
+ if (len == 0) {
+ return 1;
}
- return 0;
+
+ return FIPS202_Update(ctx, data, len);
}
+// SHAKE_Final is to be called once to finalize absorb and squeeze phases
+// |ctx->state| restricts consecutive calls to FIPS202_Finalize
+// Function SHAKE_Squeeze should be used for incremental XOF output
int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) {
+ if (ctx == NULL || md == NULL) {
+ return 0;
+ }
+
ctx->md_size = len;
- return SHA3_Final(md, ctx);
+ if (ctx->md_size == 0) {
+ return 1;
+ }
+
+ if (FIPS202_Finalize(md, ctx) == 0) {
+ return 0;
+ }
+
+ Keccak1600_Squeeze(ctx->A, md, ctx->md_size, ctx->block_size, ctx->state);
+ ctx->state = KECCAK1600_STATE_FINAL;
+
+ FIPS_service_indicator_update_state();
+
+ return 1;
+}
+
+// SHAKE_Squeeze can be called multiple time for incremental XOF output
+int SHAKE_Squeeze(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) {
+ if (ctx == NULL || md == NULL) {
+ return 0;
+ }
+
+ ctx->md_size = len;
+
+ if (ctx->md_size == 0) {
+ return 1;
+ }
+
+ if (ctx->state == KECCAK1600_STATE_FINAL) {
+ return 0;
+ }
+
+ // Skip FIPS202_Finalize if the input has been padded and
+ // the last block has been processed
+ if (ctx->state == KECCAK1600_STATE_ABSORB) {
+ if (FIPS202_Finalize(md, ctx) == 0) {
+ return 0;
+ }
+ }
+
+ Keccak1600_Squeeze(ctx->A, md, len, ctx->block_size, ctx->state);
+ ctx->state = KECCAK1600_STATE_SQUEEZE;
+
+ //FIPS_service_indicator_update_state();
+ return 1;
}
diff --git a/crypto/fipsmodule/sha/sha3_test.cc b/crypto/fipsmodule/sha/sha3_test.cc
index 9e45ddf7d1..310a5af9c3 100644
--- a/crypto/fipsmodule/sha/sha3_test.cc
+++ b/crypto/fipsmodule/sha/sha3_test.cc
@@ -165,7 +165,6 @@ TEST(SHA3Test, NISTTestVectors) {
});
}
-
TEST(SHA3Test, NISTTestVectors_SingleShot) {
FileTestGTest("crypto/fipsmodule/sha/testvectors/SHA3_224ShortMsg.txt",
[](FileTest *t) {
diff --git a/crypto/ml_dsa/ml_dsa_ref/poly.c b/crypto/ml_dsa/ml_dsa_ref/poly.c
index 42809f98e3..72d81d2e8d 100644
--- a/crypto/ml_dsa/ml_dsa_ref/poly.c
+++ b/crypto/ml_dsa/ml_dsa_ref/poly.c
@@ -316,9 +316,9 @@ void ml_dsa_poly_uniform(ml_dsa_poly *a,
t[1] = nonce >> 8;
SHAKE_Init(&state, SHAKE128_BLOCKSIZE);
- SHA3_Update(&state, seed, ML_DSA_SEEDBYTES);
- SHA3_Update(&state, t, 2);
- SHAKE_Final(buf, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE);
+ SHAKE_Absorb(&state, seed, ML_DSA_SEEDBYTES);
+ SHAKE_Absorb(&state, t, 2);
+ SHAKE_Squeeze(buf, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE);
ctr = ml_dsa_rej_uniform(a->coeffs, ML_DSA_N, buf, buflen);
@@ -327,7 +327,7 @@ void ml_dsa_poly_uniform(ml_dsa_poly *a,
for(i = 0; i < off; ++i)
buf[i] = buf[buflen - off + i];
- SHAKE_Final(buf + off, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE);
+ SHAKE_Squeeze(buf + off, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE);
buflen = SHAKE128_BLOCKSIZE + off;
ctr += ml_dsa_rej_uniform(a->coeffs + ctr, ML_DSA_N - ctr, buf, buflen);
}
@@ -418,16 +418,17 @@ void ml_dsa_poly_uniform_eta(ml_dsa_params *params,
t[1] = nonce >> 8;
SHAKE_Init(&state, SHAKE256_BLOCKSIZE);
- SHA3_Update(&state, seed, ML_DSA_CRHBYTES);
- SHA3_Update(&state, t, 2);
- SHAKE_Final(buf, &state, ML_DSA_POLY_UNIFORM_ETA_NBLOCKS_MAX * SHAKE256_BLOCKSIZE);
+ SHAKE_Absorb(&state, seed, ML_DSA_CRHBYTES);
+ SHAKE_Absorb(&state, t, 2);
+ SHAKE_Squeeze(buf, &state, ML_DSA_POLY_UNIFORM_ETA_NBLOCKS_MAX * SHAKE256_BLOCKSIZE);
ctr = rej_eta(params, a->coeffs, ML_DSA_N, buf, buflen);
while(ctr < ML_DSA_N) {
- SHAKE_Final(buf, &state, SHAKE256_BLOCKSIZE);
+ SHAKE_Squeeze(buf, &state, SHAKE256_BLOCKSIZE);
ctr += rej_eta(params, a->coeffs + ctr, ML_DSA_N - ctr, buf, SHAKE256_BLOCKSIZE);
}
+
/* FIPS 204. Section 3.6.3 Destruction of intermediate values. */
OPENSSL_cleanse(buf, sizeof(buf));
OPENSSL_cleanse(&state, sizeof(state));
@@ -459,9 +460,8 @@ void ml_dsa_poly_uniform_gamma1(ml_dsa_params *params,
t[1] = nonce >> 8;
SHAKE_Init(&state, SHAKE256_BLOCKSIZE);
- SHA3_Update(&state, seed, ML_DSA_CRHBYTES);
- SHA3_Update(&state, t, 2);
-
+ SHAKE_Absorb(&state, seed, ML_DSA_CRHBYTES);
+ SHAKE_Absorb(&state, t, 2);
SHAKE_Final(buf, &state, POLY_UNIFORM_GAMMA1_NBLOCKS * SHAKE256_BLOCKSIZE);
ml_dsa_polyz_unpack(params, a, buf);
/* FIPS 204. Section 3.6.3 Destruction of intermediate values. */
@@ -487,8 +487,8 @@ void ml_dsa_poly_challenge(ml_dsa_params *params, ml_dsa_poly *c, const uint8_t
KECCAK1600_CTX state;
SHAKE_Init(&state, SHAKE256_BLOCKSIZE);
- SHA3_Update(&state, seed, params->c_tilde_bytes);
- SHAKE_Final(buf, &state, SHAKE256_BLOCKSIZE);
+ SHAKE_Absorb(&state, seed, params->c_tilde_bytes);
+ SHAKE_Squeeze(buf, &state, SHAKE256_BLOCKSIZE);
signs = 0;
for(i = 0; i < 8; ++i) {
@@ -502,7 +502,7 @@ void ml_dsa_poly_challenge(ml_dsa_params *params, ml_dsa_poly *c, const uint8_t
for(i = ML_DSA_N-params->tau; i < ML_DSA_N; ++i) {
do {
if(pos >= SHAKE256_BLOCKSIZE) {
- SHAKE_Final(buf, &state, SHAKE256_BLOCKSIZE);
+ SHAKE_Squeeze(buf, &state, SHAKE256_BLOCKSIZE);
pos = 0;
}
diff --git a/crypto/ml_dsa/ml_dsa_ref/sign.c b/crypto/ml_dsa/ml_dsa_ref/sign.c
index 64f4110656..0573eb1b01 100644
--- a/crypto/ml_dsa/ml_dsa_ref/sign.c
+++ b/crypto/ml_dsa/ml_dsa_ref/sign.c
@@ -171,9 +171,9 @@ int ml_dsa_sign_internal(ml_dsa_params *params,
/* FIPS 204: line 7 Compute rhoprime = CRH(key, rnd, mu) */
SHAKE_Init(&state, SHAKE256_BLOCKSIZE);
- SHA3_Update(&state, key, ML_DSA_SEEDBYTES);
- SHA3_Update(&state, rnd, ML_DSA_RNDBYTES);
- SHA3_Update(&state, mu, ML_DSA_CRHBYTES);
+ SHAKE_Absorb(&state, key, ML_DSA_SEEDBYTES);
+ SHAKE_Absorb(&state, rnd, ML_DSA_RNDBYTES);
+ SHAKE_Absorb(&state, mu, ML_DSA_CRHBYTES);
SHAKE_Final(rhoprime, &state, ML_DSA_CRHBYTES);
/* FIPS 204: line 5 Expand matrix and transform vectors */
@@ -199,8 +199,8 @@ int ml_dsa_sign_internal(ml_dsa_params *params,
ml_dsa_polyveck_pack_w1(params, sig, &w1);
SHAKE_Init(&state, SHAKE256_BLOCKSIZE);
- SHA3_Update(&state, mu, ML_DSA_CRHBYTES);
- SHA3_Update(&state, sig, params->k * params->poly_w1_packed_bytes);
+ SHAKE_Absorb(&state, mu, ML_DSA_CRHBYTES);
+ SHAKE_Absorb(&state, sig, params->k * params->poly_w1_packed_bytes);
SHAKE_Final(sig, &state, params->c_tilde_bytes);
ml_dsa_poly_challenge(params, &cp, sig);
ml_dsa_poly_ntt(&cp);
@@ -471,9 +471,10 @@ int ml_dsa_verify_internal(ml_dsa_params *params,
/* FIPS 204: line 12 Call random oracle and verify challenge */
SHAKE_Init(&state, SHAKE256_BLOCKSIZE);
- SHA3_Update(&state, mu, ML_DSA_CRHBYTES);
- SHA3_Update(&state, buf, params->k * params->poly_w1_packed_bytes);
+ SHAKE_Absorb(&state, mu, ML_DSA_CRHBYTES);
+ SHAKE_Absorb(&state, buf, params->k * params->poly_w1_packed_bytes);
SHAKE_Final(c2, &state, params->c_tilde_bytes);
+
for(i = 0; i < params->c_tilde_bytes; ++i) {
if(c[i] != c2[i]) {
return -1;