summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorslontis <shane.lontis@oracle.com>2023-07-21 15:05:38 +1000
committerTomas Mraz <tomas@openssl.org>2023-11-10 13:27:00 +0100
commit536649082212e7c643ab8d7bab89f620fbcd37f0 (patch)
treed5f28d382eb86111b2d2672db4f7ab0a836bc9c5
parent9257a89b6f25dfa5aeee7114baec8ea992fcf5e5 (diff)
Add EVP_DigestSqueeze() API.
Fixes #7894 This allows SHAKE to squeeze multiple times with different output sizes. The existing EVP_DigestFinalXOF() API has been left as a one shot operation. A similar interface is used by another toolkit. The low level SHA3_Squeeze() function needed to change slightly so that it can handle multiple squeezes. This involves changing the assembler code so that it passes a boolean to indicate whether the Keccak function should be called on entry. At the provider level, the squeeze is buffered, so that it only requests a multiple of the blocksize when SHA3_Squeeze() is called. On the first call the value is zero, on subsequent calls the value passed is 1. This PR is derived from the excellent work done by @nmathewson in https://github.com/openssl/openssl/pull/7921 Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21511)
-rw-r--r--crypto/evp/digest.c37
-rw-r--r--crypto/evp/legacy_sha.c3
-rwxr-xr-xcrypto/sha/asm/keccak1600-armv4.pl4
-rwxr-xr-xcrypto/sha/asm/keccak1600-armv8.pl4
-rwxr-xr-xcrypto/sha/asm/keccak1600-ppc64.pl3
-rwxr-xr-xcrypto/sha/asm/keccak1600-x86_64.pl18
-rw-r--r--crypto/sha/keccak1600.c19
-rw-r--r--crypto/sha/sha3.c100
-rw-r--r--doc/life-cycles/digest.dot24
-rw-r--r--doc/man3/EVP_DigestInit.pod21
-rw-r--r--doc/man7/EVP_MD-BLAKE2.pod11
-rw-r--r--doc/man7/EVP_MD-SHAKE.pod15
-rw-r--r--doc/man7/img/digest.pngbin56894 -> 84676 bytes
-rw-r--r--doc/man7/life_cycle-digest.pod130
-rw-r--r--doc/man7/provider-digest.pod3
-rw-r--r--include/crypto/evp.h1
-rw-r--r--include/internal/sha3.h17
-rw-r--r--include/openssl/core_dispatch.h4
-rw-r--r--include/openssl/evp.h6
-rw-r--r--providers/implementations/digests/sha3_prov.c114
-rw-r--r--test/build.info7
-rw-r--r--test/evp_xof_test.c492
-rw-r--r--test/recipes/30-test_evp_xof.t12
-rw-r--r--util/libcrypto.num1
24 files changed, 938 insertions, 108 deletions
diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c
index 42331703da..ab670a8f49 100644
--- a/crypto/evp/digest.c
+++ b/crypto/evp/digest.c
@@ -502,6 +502,7 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *isize)
return ret;
}
+/* This is a one shot operation */
int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
{
int ret = 0;
@@ -526,10 +527,15 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
return 0;
}
+ /*
+ * For backward compatibility we pass the XOFLEN via a param here so that
+ * older providers can use the supplied value. Ideally we should have just
+ * used the size passed into ctx->digest->dfinal().
+ */
params[i++] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &size);
params[i++] = OSSL_PARAM_construct_end();
- if (EVP_MD_CTX_set_params(ctx, params) > 0)
+ if (EVP_MD_CTX_set_params(ctx, params) >= 0)
ret = ctx->digest->dfinal(ctx->algctx, md, &size, size);
ctx->flags |= EVP_MD_CTX_FLAG_FINALISED;
@@ -553,6 +559,27 @@ legacy:
return ret;
}
+/* EVP_DigestSqueeze() can be called multiple times */
+int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
+{
+ if (ctx->digest == NULL) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM);
+ return 0;
+ }
+
+ if (ctx->digest->prov == NULL) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_OPERATION);
+ return 0;
+ }
+
+ if (ctx->digest->dsqueeze == NULL) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_METHOD_NOT_SUPPORTED);
+ return 0;
+ }
+
+ return ctx->digest->dsqueeze(ctx->algctx, md, &size, size);
+}
+
EVP_MD_CTX *EVP_MD_CTX_dup(const EVP_MD_CTX *in)
{
EVP_MD_CTX *out = EVP_MD_CTX_new();
@@ -1032,6 +1059,12 @@ static void *evp_md_from_algorithm(int name_id,
fncnt++;
}
break;
+ case OSSL_FUNC_DIGEST_SQUEEZE:
+ if (md->dsqueeze == NULL) {
+ md->dsqueeze = OSSL_FUNC_digest_squeeze(fns);
+ fncnt++;
+ }
+ break;
case OSSL_FUNC_DIGEST_DIGEST:
if (md->digest == NULL)
md->digest = OSSL_FUNC_digest_digest(fns);
@@ -1075,7 +1108,7 @@ static void *evp_md_from_algorithm(int name_id,
break;
}
}
- if ((fncnt != 0 && fncnt != 5)
+ if ((fncnt != 0 && fncnt != 5 && fncnt != 6)
|| (fncnt == 0 && md->digest == NULL)) {
/*
* In order to be a consistent set of functions we either need the
diff --git a/crypto/evp/legacy_sha.c b/crypto/evp/legacy_sha.c
index 0c2afc2900..38423ff540 100644
--- a/crypto/evp/legacy_sha.c
+++ b/crypto/evp/legacy_sha.c
@@ -37,7 +37,8 @@ static int nm##_update(EVP_MD_CTX *ctx, const void *data, size_t count) \
} \
static int nm##_final(EVP_MD_CTX *ctx, unsigned char *md) \
{ \
- return fn##_final(md, EVP_MD_CTX_get0_md_data(ctx)); \
+ KECCAK1600_CTX *kctx = EVP_MD_CTX_get0_md_data(ctx); \
+ return fn##_final(kctx, md, kctx->md_size); \
}
#define IMPLEMENT_LEGACY_EVP_MD_METH_SHAKE(nm, fn, tag) \
static int nm##_init(EVP_MD_CTX *ctx) \
diff --git a/crypto/sha/asm/keccak1600-armv4.pl b/crypto/sha/asm/keccak1600-armv4.pl
index eaad86d39d..18948fd7c0 100755
--- a/crypto/sha/asm/keccak1600-armv4.pl
+++ b/crypto/sha/asm/keccak1600-armv4.pl
@@ -966,6 +966,8 @@ SHA3_squeeze:
stmdb sp!,{r6-r9}
mov r14,$A_flat
+ cmp r4, #0 @ r4 = 'next' argument
+ bne .Lnext_block
b .Loop_squeeze
.align 4
@@ -1037,7 +1039,7 @@ SHA3_squeeze:
subs $bsz,$bsz,#8 @ bsz -= 8
bhi .Loop_squeeze
-
+.Lnext_block:
mov r0,r14 @ original $A_flat
bl KeccakF1600
diff --git a/crypto/sha/asm/keccak1600-armv8.pl b/crypto/sha/asm/keccak1600-armv8.pl
index ab7aa713ac..72f8c3adb5 100755
--- a/crypto/sha/asm/keccak1600-armv8.pl
+++ b/crypto/sha/asm/keccak1600-armv8.pl
@@ -483,6 +483,8 @@ SHA3_squeeze:
mov $out,x1
mov $len,x2
mov $bsz,x3
+ cmp x4, #0 // x4 = 'next' argument
+ bne .Lnext_block
.Loop_squeeze:
ldr x4,[x0],#8
@@ -497,7 +499,7 @@ SHA3_squeeze:
subs x3,x3,#8
bhi .Loop_squeeze
-
+.Lnext_block:
mov x0,$A_flat
bl KeccakF1600
mov x0,$A_flat
diff --git a/crypto/sha/asm/keccak1600-ppc64.pl b/crypto/sha/asm/keccak1600-ppc64.pl
index bff0d78585..3f8ba817f8 100755
--- a/crypto/sha/asm/keccak1600-ppc64.pl
+++ b/crypto/sha/asm/keccak1600-ppc64.pl
@@ -668,6 +668,8 @@ SHA3_squeeze:
subi $out,r4,1 ; prepare for stbu
mr $len,r5
mr $bsz,r6
+ ${UCMP}i r7,1 ; r7 = 'next' argument
+ blt .Lnext_block
b .Loop_squeeze
.align 4
@@ -698,6 +700,7 @@ SHA3_squeeze:
subic. r6,r6,8
bgt .Loop_squeeze
+.Lnext_block:
mr r3,$A_flat
bl KeccakF1600
subi r3,$A_flat,8 ; prepare for ldu
diff --git a/crypto/sha/asm/keccak1600-x86_64.pl b/crypto/sha/asm/keccak1600-x86_64.pl
index 02f0116014..bddcaf8294 100755
--- a/crypto/sha/asm/keccak1600-x86_64.pl
+++ b/crypto/sha/asm/keccak1600-x86_64.pl
@@ -503,12 +503,12 @@ SHA3_absorb:
.size SHA3_absorb,.-SHA3_absorb
___
}
-{ my ($A_flat,$out,$len,$bsz) = ("%rdi","%rsi","%rdx","%rcx");
+{ my ($A_flat,$out,$len,$bsz,$next) = ("%rdi","%rsi","%rdx","%rcx","%r8");
($out,$len,$bsz) = ("%r12","%r13","%r14");
$code.=<<___;
.globl SHA3_squeeze
-.type SHA3_squeeze,\@function,4
+.type SHA3_squeeze,\@function,5
.align 32
SHA3_squeeze:
.cfi_startproc
@@ -520,10 +520,12 @@ SHA3_squeeze:
.cfi_push %r14
shr \$3,%rcx
- mov $A_flat,%r8
+ mov $A_flat,%r9
mov %rsi,$out
mov %rdx,$len
mov %rcx,$bsz
+ bt \$0,$next
+ jc .Lnext_block
jmp .Loop_squeeze
.align 32
@@ -531,8 +533,8 @@ SHA3_squeeze:
cmp \$8,$len
jb .Ltail_squeeze
- mov (%r8),%rax
- lea 8(%r8),%r8
+ mov (%r9),%rax
+ lea 8(%r9),%r9
mov %rax,($out)
lea 8($out),$out
sub \$8,$len # len -= 8
@@ -540,14 +542,14 @@ SHA3_squeeze:
sub \$1,%rcx # bsz--
jnz .Loop_squeeze
-
+.Lnext_block:
call KeccakF1600
- mov $A_flat,%r8
+ mov $A_flat,%r9
mov $bsz,%rcx
jmp .Loop_squeeze
.Ltail_squeeze:
- mov %r8, %rsi
+ mov %r9, %rsi
mov $out,%rdi
mov $len,%rcx
.byte 0xf3,0xa4 # rep movsb
diff --git a/crypto/sha/keccak1600.c b/crypto/sha/keccak1600.c
index c15bc42aaa..6682367be1 100644
--- a/crypto/sha/keccak1600.c
+++ b/crypto/sha/keccak1600.c
@@ -13,7 +13,7 @@
size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len,
size_t r);
-void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r);
+void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next);
#if !defined(KECCAK1600_ASM) || !defined(SELFTEST)
@@ -1090,10 +1090,16 @@ size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len,
}
/*
- * sha3_squeeze is called once at the end to generate |out| hash value
- * of |len| bytes.
+ * SHA3_squeeze may be called after SHA3_absorb to generate |out| hash value of
+ * |len| bytes.
+ * If multiple SHA3_squeeze calls are required the output length |len| must be a
+ * multiple of the blocksize, with |next| being 0 on the first call and 1 on
+ * subsequent calls. It is the callers responsibility to buffer the results.
+ * When only a single call to SHA3_squeeze is required, |len| can be any size
+ * and |next| must be 0.
*/
-void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r)
+void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r,
+ int next)
{
uint64_t *A_flat = (uint64_t *)A;
size_t i, w = r / 8;
@@ -1101,6 +1107,9 @@ void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r)
assert(r < (25 * sizeof(A[0][0])) && (r % 8) == 0);
while (len != 0) {
+ if (next)
+ KeccakF1600(A);
+ next = 1;
for (i = 0; i < w && len != 0; i++) {
uint64_t Ai = BitDeinterleave(A_flat[i]);
@@ -1123,8 +1132,6 @@ void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r)
out += 8;
len -= 8;
}
- if (len)
- KeccakF1600(A);
}
}
#endif
diff --git a/crypto/sha/sha3.c b/crypto/sha/sha3.c
index 633bc2e120..2411b3f1f8 100644
--- a/crypto/sha/sha3.c
+++ b/crypto/sha/sha3.c
@@ -10,12 +10,13 @@
#include <string.h>
#include "internal/sha3.h"
-void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r);
+void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next);
void ossl_sha3_reset(KECCAK1600_CTX *ctx)
{
memset(ctx->A, 0, sizeof(ctx->A));
ctx->bufsz = 0;
+ ctx->xof_state = XOF_STATE_INIT;
}
int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen)
@@ -51,6 +52,10 @@ int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len)
if (len == 0)
return 1;
+ if (ctx->xof_state == XOF_STATE_SQUEEZE
+ || ctx->xof_state == XOF_STATE_FINAL)
+ return 0;
+
if ((num = ctx->bufsz) != 0) { /* process intermediate buffer? */
rem = bsz - num;
@@ -84,13 +89,21 @@ int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len)
return 1;
}
-int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx)
+/*
+ * ossl_sha3_final()is a single shot method
+ * (Use ossl_sha3_squeeze for multiple calls).
+ * outlen is the variable size output.
+ */
+int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen)
{
size_t bsz = ctx->block_size;
size_t num = ctx->bufsz;
- if (ctx->md_size == 0)
+ if (outlen == 0)
return 1;
+ if (ctx->xof_state == XOF_STATE_SQUEEZE
+ || ctx->xof_state == XOF_STATE_FINAL)
+ return 0;
/*
* Pad the data with 10*1. Note that |num| can be |bsz - 1|
@@ -103,7 +116,86 @@ int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx)
(void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
- SHA3_squeeze(ctx->A, md, ctx->md_size, bsz);
+ ctx->xof_state = XOF_STATE_FINAL;
+ SHA3_squeeze(ctx->A, out, outlen, bsz, 0);
+ return 1;
+}
+
+/*
+ * This method can be called multiple times.
+ * Rather than heavily modifying assembler for SHA3_squeeze(),
+ * we instead just use the limitations of the existing function.
+ * i.e. Only request multiples of the ctx->block_size when calling
+ * SHA3_squeeze(). For output length requests smaller than the
+ * ctx->block_size just request a single ctx->block_size bytes and
+ * buffer the results. The next request will use the buffer first
+ * to grab output bytes.
+ */
+int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen)
+{
+ size_t bsz = ctx->block_size;
+ size_t num = ctx->bufsz;
+ size_t len;
+ int next = 1;
+
+ if (outlen == 0)
+ return 1;
+
+ if (ctx->xof_state == XOF_STATE_FINAL)
+ return 0;
+
+ /*
+ * On the first squeeze call, finish the absorb process,
+ * by adding the trailing padding and then doing
+ * a final absorb.
+ */
+ if (ctx->xof_state != XOF_STATE_SQUEEZE) {
+ /*
+ * Pad the data with 10*1. Note that |num| can be |bsz - 1|
+ * in which case both byte operations below are performed on
+ * same byte...
+ */
+ memset(ctx->buf + num, 0, bsz - num);
+ ctx->buf[num] = ctx->pad;
+ ctx->buf[bsz - 1] |= 0x80;
+ (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
+ ctx->xof_state = XOF_STATE_SQUEEZE;
+ num = ctx->bufsz = 0;
+ next = 0;
+ }
+
+ /*
+ * Step 1. Consume any bytes left over from a previous squeeze
+ * (See Step 4 below).
+ */
+ if (num != 0) {
+ if (outlen > ctx->bufsz)
+ len = ctx->bufsz;
+ else
+ len = outlen;
+ memcpy(out, ctx->buf + bsz - ctx->bufsz, len);
+ out += len;
+ outlen -= len;
+ ctx->bufsz -= len;
+ }
+ if (outlen == 0)
+ return 1;
+
+ /* Step 2. Copy full sized squeezed blocks to the output buffer directly */
+ if (outlen >= bsz) {
+ len = bsz * (outlen / bsz);
+ SHA3_squeeze(ctx->A, out, len, bsz, next);
+ next = 1;
+ out += len;
+ outlen -= len;
+ }
+ if (outlen > 0) {
+ /* Step 3. Squeeze one more block into a buffer */
+ SHA3_squeeze(ctx->A, ctx->buf, bsz, bsz, next);
+ memcpy(out, ctx->buf, outlen);
+ /* Step 4. Remember the leftover part of the squeezed block */
+ ctx->bufsz = bsz - outlen;
+ }
return 1;
}
diff --git a/doc/life-cycles/digest.dot b/doc/life-cycles/digest.dot
index 8d4d72480c..2f22a0d5e6 100644
--- a/doc/life-cycles/digest.dot
+++ b/doc/life-cycles/digest.dot
@@ -6,28 +6,30 @@ digraph digest {
initialised [label=initialised, fontcolor="#c94c4c"];
updated [label=updated, fontcolor="#c94c4c"];
finaled [label="finaled", fontcolor="#c94c4c"];
+ squeezed [label="squeezed", fontcolor="#c94c4c"];
end [label="freed", color="#deeaee", style="filled"];
begin -> newed [label="EVP_MD_CTX_new"];
- newed -> initialised [label="EVP_DigestInit"];
- initialised -> updated [label="EVP_DigestUpdate", weight=3];
+ newed -> initialised [label="EVP_DigestInit", weight=100];
+ initialised -> updated [label="EVP_DigestUpdate", weight=100];
updated -> updated [label="EVP_DigestUpdate"];
- updated -> finaled [label="EVP_DigestFinal"];
+ updated -> finaled [label="EVP_DigestFinal", weight=2];
updated -> finaled [label="EVP_DigestFinalXOF",
fontcolor="#808080", color="#808080"];
- /* Once this works it should go back in:
- finaled -> finaled [taillabel="EVP_DigestFinalXOF",
- labeldistance=9, labelangle=345,
- labelfontcolor="#808080", color="#808080"];
- */
+ updated -> squeezed [label="EVP_DigestSqueeze", weight=3];
finaled -> end [label="EVP_MD_CTX_free"];
- finaled -> newed [label="EVP_MD_CTX_reset", style=dashed, weight=2,
+ finaled -> newed [label="EVP_MD_CTX_reset", style=dashed,
color="#034f84", fontcolor="#034f84"];
updated -> newed [label="EVP_MD_CTX_reset", style=dashed,
color="#034f84", fontcolor="#034f84"];
- updated -> initialised [label="EVP_DigestInit", weight=0, style=dashed,
+ updated -> initialised [label="EVP_DigestInit", style=dashed,
color="#034f84", fontcolor="#034f84"];
finaled -> initialised [label="EVP_DigestInit", style=dashed,
color="#034f84", fontcolor="#034f84"];
+ squeezed -> squeezed [label="EVP_DigestSqueeze"];
+ squeezed -> end [label="EVP_MD_CTX_free", weight=1];
+ squeezed -> newed [label="EVP_MD_CTX_reset", style=dashed,
+ color="#034f84", fontcolor="#034f84"];
+ squeezed -> initialised [label="EVP_DigestInit", style=dashed,
+ color="#034f84", fontcolor="#034f84"];
}
-
diff --git a/doc/man3/EVP_DigestInit.pod b/doc/man3/EVP_DigestInit.pod
index 409630e5d4..492180def9 100644
--- a/doc/man3/EVP_DigestInit.pod
+++ b/doc/man3/EVP_DigestInit.pod
@@ -12,6 +12,7 @@ EVP_MD_CTX_settable_params, EVP_MD_CTX_gettable_params,
EVP_MD_CTX_set_flags, EVP_MD_CTX_clear_flags, EVP_MD_CTX_test_flags,
EVP_Q_digest, EVP_Digest, EVP_DigestInit_ex2, EVP_DigestInit_ex, EVP_DigestInit,
EVP_DigestUpdate, EVP_DigestFinal_ex, EVP_DigestFinalXOF, EVP_DigestFinal,
+EVP_DigestSqueeze,
EVP_MD_is_a, EVP_MD_get0_name, EVP_MD_get0_description,
EVP_MD_names_do_all, EVP_MD_get0_provider, EVP_MD_get_type,
EVP_MD_get_pkey_type, EVP_MD_get_size, EVP_MD_get_block_size, EVP_MD_get_flags,
@@ -61,7 +62,8 @@ EVP_MD_CTX_type, EVP_MD_CTX_pkey_ctx, EVP_MD_CTX_md_data
int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);
int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt);
int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s);
- int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t len);
+ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *out, size_t outlen);
+ int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *out, size_t outlen);
EVP_MD_CTX *EVP_MD_CTX_dup(const EVP_MD_CTX *in);
int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in);
@@ -291,9 +293,16 @@ initialize a new digest operation.
=item EVP_DigestFinalXOF()
Interfaces to extendable-output functions, XOFs, such as SHAKE128 and SHAKE256.
-It retrieves the digest value from I<ctx> and places it in I<len>-sized I<md>.
+It retrieves the digest value from I<ctx> and places it in I<outlen>-sized I<out>.
After calling this function no additional calls to EVP_DigestUpdate() can be
made, but EVP_DigestInit_ex2() can be called to initialize a new operation.
+EVP_DigestFinalXOF() may only be called once
+
+=item EVP_DigestSqueeze()
+
+Similar to EVP_DigestFinalXOF() but allows multiple calls to be made to
+squeeze variable length output data.
+EVP_DigestFinalXOF() should not be called after this.
=item EVP_MD_CTX_dup()
@@ -478,8 +487,9 @@ EVP_MD_CTX_set_params() can be used with the following OSSL_PARAM keys:
=item "xoflen" (B<OSSL_DIGEST_PARAM_XOFLEN>) <unsigned integer>
Sets the digest length for extendable output functions.
-It is used by the SHAKE algorithm and should not exceed what can be given
-using a B<size_t>.
+The value should not exceed what can be given using a B<size_t>.
+It may be used by BLAKE2B-512, SHAKE-128 and SHAKE-256 to set the
+output length used by EVP_DigestFinal_ex() and EVP_DigestFinal().
=item "pad-type" (B<OSSL_DIGEST_PARAM_PAD_TYPE>) <unsigned integer>
@@ -795,7 +805,8 @@ EVP_MD_CTX_get0_md() instead.
EVP_MD_CTX_update_fn() and EVP_MD_CTX_set_update_fn() were deprecated
in OpenSSL 3.0.
-EVP_MD_CTX_dup() was added in OpenSSL 3.2.
+The functions EVP_MD_CTX_dup() and EVP_DigestSqueeze() were added in
+OpenSSL 3.2.
=head1 COPYRIGHT
diff --git a/doc/man7/EVP_MD-BLAKE2.pod b/doc/man7/EVP_MD-BLAKE2.pod
index 205b0c59be..288a6dd735 100644
--- a/doc/man7/EVP_MD-BLAKE2.pod
+++ b/doc/man7/EVP_MD-BLAKE2.pod
@@ -25,6 +25,17 @@ Known names are "BLAKE2B-512" and "BLAKE2b512".
=back
+=head2 Settable Parameters
+
+"BLAKE2B-512" supports the following EVP_MD_CTX_set_params() key
+described in L<EVP_DigestInit(3)/PARAMETERS>.
+
+=over 4
+
+=item "xoflen" (B<OSSL_DIGEST_PARAM_XOFLEN>) <unsigned integer>
+
+=back
+
=head2 Gettable Parameters
This implementation supports the common gettable parameters described
diff --git a/doc/man7/EVP_MD-SHAKE.pod b/doc/man7/EVP_MD-SHAKE.pod
index 8a31cd53a8..6f1fe9cae6 100644
--- a/doc/man7/EVP_MD-SHAKE.pod
+++ b/doc/man7/EVP_MD-SHAKE.pod
@@ -65,15 +65,28 @@ For backwards compatibility reasons the default xoflen length for SHAKE-256 is
32 (bytes) which results in a security strength of only 128 bits. To ensure the
maximum security strength of 256 bits, the xoflen should be set to at least 64.
+This parameter may be used when calling either EVP_DigestFinal_ex() or
+EVP_DigestFinal(), since these functions were not designed to handle variable
+length output. It is recommended to either use EVP_DigestSqueeze() or
+EVP_DigestFinalXOF() instead.
+
=back
+=head1 NOTES
+
+For SHAKE-128, to ensure the maximum security strength of 128 bits, the output
+length passed to EVP_DigestFinalXOF() should be at least 32.
+
+For SHAKE-256, to ensure the maximum security strength of 256 bits, the output
+length passed to EVP_DigestFinalXOF() should be at least 64.
+
=head1 SEE ALSO
L<EVP_MD_CTX_set_params(3)>, L<provider-digest(7)>, L<OSSL_PROVIDER-default(7)>
=head1 COPYRIGHT
-Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
diff --git a/doc/man7/img/digest.png b/doc/man7/img/digest.png
index 9f35deb5dc..8a9f78a26b 100644
--- a/doc/man7/img/digest.png
+++ b/doc/man7/img/digest.png
Binary files differ
diff --git a/doc/man7/life_cycle-digest.pod b/doc/man7/life_cycle-digest.pod
index 709fd0d04c..672e61c7e4 100644
--- a/doc/man7/life_cycle-digest.pod
+++ b/doc/man7/life_cycle-digest.pod
@@ -32,6 +32,14 @@ additional input or generating output.
=item finaled
This state represents the MD when it has generated output.
+For an XOF digest, this state represents the MD when it has generated a
+single-shot output.
+
+=item squeezed
+
+For an XOF digest, this state represents the MD when it has generated output.
+It can be called multiple times to generate more output. The output length is
+variable for each call.
=item freed
@@ -46,39 +54,57 @@ The usual life-cycle of a MD is illustrated:
=begin man
- +-------------------+
- | start |
- +-------------------+
- |
- | EVP_MD_CTX_new
- v
- +-------------------+ EVP_MD_CTX_reset
- | newed | <------------------------------+
- +-------------------+ |
- | |
- | EVP_DigestInit |
- v |
- +-------------------+ |
- +--> | initialised | <+ EVP_DigestInit |
- | +-------------------+ | |
- | | | EVP_DigestUpdate |
- | | EVP_DigestUpdate | +------------------+ |
- | v | v | |
- | +------------------------------------------------+ |
- EVP_DigestInit | | updated | --+
- | +------------------------------------------------+ |
- | | | |
- | | EVP_DigestFinal | EVP_DigestFinalXOF |
- | v v |
- | +------------------------------------------------+ |
- +--- | finaled | --+
- +------------------------------------------------+
- |
- | EVP_MD_CTX_free
- v
- +-------------------+
- | freed |
- +-------------------+
+ +--------------------+
+ | start |
+ +--------------------+
+ | EVP_MD_CTX_reset
+ | EVP_MD_CTX_new +-------------------------------------------------+
+ v v |
+ EVP_MD_CTX_reset + - - - - - - - - - - - - - - - - - - - - - - + EVP_MD_CTX_reset |
+ +-------------------> ' newed ' <--------------------+ |
+ | + - - - - - - - - - - - - - - - - - - - - - - + | |
+ | | | |
+ | | EVP_DigestInit | |
+ | v | |
+ | EVP_DigestInit + - - - - - - - - - - - - - - - - - - - - - - + | |
+ +----+-------------------> ' initialised ' <+ EVP_DigestInit | |
+ | | + - - - - - - - - - - - - - - - - - - - - - - + | | |
+ | | | ^ | | |
+ | | | EVP_DigestUpdate | EVP_DigestInit | | |
+ | | v | | | |
+ | | +---------------------------------------------+ | | |
+ | +-------------------- | | | | |
+ | | | | | |
+ | EVP_DigestUpdate | | | | |
+ | +-------------------- | | | | |
+ | | | updated | | | |
+ | +-------------------> | | | | |
+ | | | | | |
+ | | | | | |
+ +----+------------------------- | | -+-------------------+----+ |
+ | | +---------------------------------------------+ | | | |
+ | | | | | | |
+ | | | EVP_DigestSqueeze +-------------------+ | | |
+ | | v | | | |
+ | | EVP_DigestSqueeze +---------------------------------------------+ | | |
+ | | +-------------------- | | | | |
+ | | | | squeezed | | | |
+ | | +-------------------> | | ---------------------+ | |
+ | | +---------------------------------------------+ | |
+ | | | | |
+ | | +---------------------------------------+ | |
+ | | | | |
+ | | +---------------------------------------------+ EVP_DigestFinalXOF | | |
+ | +------------------------- | finaled | <--------------------+----+ |
+ | +---------------------------------------------+ | |
+ | EVP_DigestFinal ^ | | | |
+ +---------------------------------+ | | EVP_MD_CTX_free | |
+ | v | |
+ | +------------------+ EVP_MD_CTX_free | |
+ | | freed | <--------------------+ |
+ | +------------------+ |
+ | |
+ +------------------------------------------------------+
=end man
@@ -91,19 +117,21 @@ This is the canonical list.
=begin man
- Function Call --------------------- Current State ----------------------
- start newed initialised updated finaled freed
+ Function Call --------------------- Current State -----------------------------------
+ start newed initialised updated finaled squeezed freed
EVP_MD_CTX_new newed
- EVP_DigestInit initialised initialised initialised initialised
+ EVP_DigestInit initialised initialised initialised initialised initialised
EVP_DigestUpdate updated updated
EVP_DigestFinal finaled