]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add EVP_DigestSqueeze() API.
authorslontis <shane.lontis@oracle.com>
Fri, 21 Jul 2023 05:05:38 +0000 (15:05 +1000)
committerTomas Mraz <tomas@openssl.org>
Fri, 10 Nov 2023 12:27:00 +0000 (13:27 +0100)
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)

24 files changed:
crypto/evp/digest.c
crypto/evp/legacy_sha.c
crypto/sha/asm/keccak1600-armv4.pl
crypto/sha/asm/keccak1600-armv8.pl
crypto/sha/asm/keccak1600-ppc64.pl
crypto/sha/asm/keccak1600-x86_64.pl
crypto/sha/keccak1600.c
crypto/sha/sha3.c
doc/life-cycles/digest.dot
doc/man3/EVP_DigestInit.pod
doc/man7/EVP_MD-BLAKE2.pod
doc/man7/EVP_MD-SHAKE.pod
doc/man7/img/digest.png
doc/man7/life_cycle-digest.pod
doc/man7/provider-digest.pod
include/crypto/evp.h
include/internal/sha3.h
include/openssl/core_dispatch.h
include/openssl/evp.h
providers/implementations/digests/sha3_prov.c
test/build.info
test/evp_xof_test.c [new file with mode: 0644]
test/recipes/30-test_evp_xof.t [new file with mode: 0644]
util/libcrypto.num

index 42331703da3223fda7107f679df23256bb2ab016..ab670a8f49cb688b70785afce709ed4df5bd5c3e 100644 (file)
@@ -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
index 0c2afc29007597e947785c60e50ac2984948a46c..38423ff540f5685f19ff695987295d62ec7b3c37 100644 (file)
@@ -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)                                          \
index eaad86d39ddf7037de4a02fb0df2e1f63dbc9996..18948fd7c0d264a80f8ac0bfe3957e6124472c1e 100755 (executable)
@@ -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
index ab7aa713acd3464f6ccdad4844d97d4cf81e0685..72f8c3adb577bfa70ecf5f329fd8708f5efcac3a 100755 (executable)
@@ -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
index bff0d785859e3ba9d91858a9b69ebab27b141473..3f8ba817f8d51420af4a9e935b45313ccb37a2a6 100755 (executable)
@@ -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
index 02f0116014d68d2e9fa3e13058b065cd7c331eba..bddcaf8294192ba927af37c5e1bd3339f451f68a 100755 (executable)
@@ -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
index c15bc42aaa826cc8aa176d8ebfd82ab939ab9ba2..6682367be19b7ba3fa0691fd22e8a74f34bce3e4 100644 (file)
@@ -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
index 633bc2e1208b5c56dd8422f7b169f2020d50d89a..2411b3f1f8e2feeb8d02538d878261a58f303a96 100644 (file)
 #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;
 }
index 8d4d72480c9accda24a287ad0a870598c85def5f..2f22a0d5e69534b77542227b0271d587c23f70b2 100644 (file)
@@ -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"];
 }
-
index 409630e5d4db88105241d5c5a91d0e084e556494..492180def977574c230417d06bcfa7bdc52bf434 100644 (file)
@@ -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
 
index 205b0c59be6f339ba1d61cf92fcdad86fcfdea0b..288a6dd735323f51e3c962aa218354d311b0bbdb 100644 (file)
@@ -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
index 8a31cd53a8ba3922786787a94b98413b22bfb830..6f1fe9cae64f7a320a0f2062957ba8f502cf48ae 100644 (file)
@@ -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
index 9f35deb5dcc8c03c8631e7d60267e9bbcb1b6a9a..8a9f78a26b39b0e091095fa0b67a5b14d4bc153c 100644 (file)
Binary files a/doc/man7/img/digest.png and b/doc/man7/img/digest.png differ
index 709fd0d04ce7eb803dce51aefc5bebe726fb4077..672e61c7e4b8e9f8b77e1079899e13a8b7d6c679 100644 (file)
@@ -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
  EVP_DigestFinalXOF                                          finaled
+ EVP_DigestSqueeze                                           squeezed                squeezed
  EVP_MD_CTX_free              freed   freed       freed       freed       freed
  EVP_MD_CTX_reset                     newed       newed       newed       newed
  EVP_MD_CTX_get_params                newed    initialised   updated
  EVP_MD_CTX_set_params                newed    initialised   updated
  EVP_MD_CTX_gettable_params           newed    initialised   updated
  EVP_MD_CTX_settable_params           newed    initialised   updated
+ EVP_MD_CTX_copy_ex                   newed    initialised   updated                 squeezed
 
 =end man
 
@@ -118,6 +146,7 @@ This is the canonical list.
     <th style="border:1px solid" align="center">initialised</th>
     <th style="border:1px solid" align="center">updated</th>
     <th style="border:1px solid" align="center">finaled</th>
+    <th style="border:1px solid" align="center">squeezed</th>
     <th style="border:1px solid" align="center">freed</th></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_new</th>
     <td style="border:1px solid" align="center">newed</td>
@@ -125,6 +154,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_DigestInit</th>
     <td style="border:1px solid" align="center"></td>
@@ -132,6 +162,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center">initialised</td>
+    <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_DigestUpdate</th>
     <td style="border:1px solid" align="center"></td>
@@ -139,6 +170,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">updated</td>
     <td style="border:1px solid" align="center">updated</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_DigestFinal</th>
     <td style="border:1px solid" align="center"></td>
@@ -146,6 +178,15 @@ This is the canonical list.
     <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center">finaled</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td></tr>
+<tr><th style="border:1px solid" align="left">EVP_DigestSqueeze</th>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center">squeezed</td>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center">squeezed</td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_DigestFinalXOF</th>
     <td style="border:1px solid" align="center"></td>
@@ -153,6 +194,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center">finaled</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_free</th>
     <td style="border:1px solid" align="center">freed</td>
@@ -160,6 +202,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">freed</td>
     <td style="border:1px solid" align="center">freed</td>
     <td style="border:1px solid" align="center">freed</td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_reset</th>
     <td style="border:1px solid" align="center"></td>
@@ -167,6 +210,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">newed</td>
     <td style="border:1px solid" align="center">newed</td>
     <td style="border:1px solid" align="center">newed</td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_get_params</th>
     <td style="border:1px solid" align="center"></td>
@@ -174,6 +218,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center">updated</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_set_params</th>
     <td style="border:1px solid" align="center"></td>
@@ -181,6 +226,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center">updated</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_gettable_params</th>
     <td style="border:1px solid" align="center"></td>
@@ -188,6 +234,7 @@ This is the canonical list.
     <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center">updated</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
     <td style="border:1px solid" align="center"></td></tr>
 <tr><th style="border:1px solid" align="left">EVP_MD_CTX_settable_params</th>
     <td style="border:1px solid" align="center"></td>
@@ -195,6 +242,15 @@ This is the canonical list.
     <td style="border:1px solid" align="center">initialised</td>
     <td style="border:1px solid" align="center">updated</td>
     <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center"></td></tr>
+<tr><th style="border:1px solid" align="left">EVP_MD_CTX_copy_ex</th>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center">newed</td>
+    <td style="border:1px solid" align="center">initialised</td>
+    <td style="border:1px solid" align="center">updated</td>
+    <td style="border:1px solid" align="center"></td>
+    <td style="border:1px solid" align="center">squeezed</td>
     <td style="border:1px solid" align="center"></td></tr>
 </table>
 
@@ -211,7 +267,7 @@ L<provider-digest(7)>, L<EVP_DigestInit(3)>
 
 =head1 COPYRIGHT
 
-Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2021-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
index 2c99b8b3fb24352ba8b7781b624ff884acc6bd02..d23da59e1a0218dfefc283fd79baa28d4adc248b 100644 (file)
@@ -198,8 +198,7 @@ This digest method can only handle one block of input.
 
 =item B<EVP_MD_FLAG_XOF>
 
-This digest method is an extensible-output function (XOF) and supports
-setting the B<OSSL_DIGEST_PARAM_XOFLEN> parameter.
+This digest method is an extensible-output function (XOF).
 
 =item B<EVP_MD_FLAG_DIGALGID_NULL>
 
index 34cea2f9f4a0c564e27aea11f789d5c86f7d2d23..96133bf7f59c69b45518c9ab3dc068039bd7f06e 100644 (file)
@@ -282,6 +282,7 @@ struct evp_md_st {
     OSSL_FUNC_digest_init_fn *dinit;
     OSSL_FUNC_digest_update_fn *dupdate;
     OSSL_FUNC_digest_final_fn *dfinal;
+    OSSL_FUNC_digest_squeeze_fn *dsqueeze;
     OSSL_FUNC_digest_digest_fn *digest;
     OSSL_FUNC_digest_freectx_fn *freectx;
     OSSL_FUNC_digest_dupctx_fn *dupctx;
index 80ad86e58e3e2ad5327fb64a26a4fd22f57548b3..332916aa54737e591fb7422487abbb1f8d0eed7e 100644 (file)
 
 typedef struct keccak_st KECCAK1600_CTX;
 
-typedef size_t (sha3_absorb_fn)(void *vctx, const void *inp, size_t len);
-typedef int (sha3_final_fn)(unsigned char *md, void *vctx);
+typedef size_t (sha3_absorb_fn)(void *vctx, const void *in, size_t inlen);
+typedef int (sha3_final_fn)(void *vctx, unsigned char *out, size_t outlen);
+typedef int (sha3_squeeze_fn)(void *vctx, unsigned char *out, size_t outlen);
 
 typedef struct prov_sha3_meth_st
 {
     sha3_absorb_fn *absorb;
     sha3_final_fn *final;
+    sha3_squeeze_fn *squeeze;
 } PROV_SHA3_METHOD;
 
+#define XOF_STATE_INIT    0
+#define XOF_STATE_ABSORB  1
+#define XOF_STATE_FINAL   2
+#define XOF_STATE_SQUEEZE 3
+
 struct keccak_st {
     uint64_t A[5][5];
+    unsigned char buf[KECCAK1600_WIDTH / 8 - 32];
     size_t block_size;          /* cached ctx->digest->block_size */
     size_t md_size;             /* output length, variable in XOF */
     size_t bufsz;               /* used bytes in below buffer */
-    unsigned char buf[KECCAK1600_WIDTH / 8 - 32];
     unsigned char pad;
     PROV_SHA3_METHOD meth;
+    int xof_state;
 };
 
 void ossl_sha3_reset(KECCAK1600_CTX *ctx);
@@ -46,7 +54,8 @@ 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_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len);
-int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx);
+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);
 
 size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len,
                    size_t r);
index 9b03f20c3b7da4fe8939fd8b48af472453c4b45d..a5bc2cf75d345829101a1b3e3ca7999429560d79 100644 (file)
@@ -300,6 +300,7 @@ OSSL_CORE_MAKE_FUNC(int, provider_self_test, (void *provctx))
 # define OSSL_FUNC_DIGEST_GETTABLE_PARAMS           11
 # define OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS       12
 # define OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS       13
+# define OSSL_FUNC_DIGEST_SQUEEZE                   14
 
 OSSL_CORE_MAKE_FUNC(void *, digest_newctx, (void *provctx))
 OSSL_CORE_MAKE_FUNC(int, digest_init, (void *dctx, const OSSL_PARAM params[]))
@@ -308,6 +309,9 @@ OSSL_CORE_MAKE_FUNC(int, digest_update,
 OSSL_CORE_MAKE_FUNC(int, digest_final,
                     (void *dctx,
                      unsigned char *out, size_t *outl, size_t outsz))
+OSSL_CORE_MAKE_FUNC(int, digest_squeeze,
+                    (void *dctx,
+                     unsigned char *out, size_t *outl, size_t outsz))
 OSSL_CORE_MAKE_FUNC(int, digest_digest,
                     (void *provctx, const unsigned char *in, size_t inl,
                      unsigned char *out, size_t *outl, size_t outsz))
index ea7620d63159d41e580b9e1fc4cf5280d7758947..f70b9d744d63ae28f7750898249ef56fcfc7913d 100644 (file)
@@ -729,8 +729,10 @@ __owur int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in);
 __owur int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);
 __owur int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md,
                            unsigned int *s);
-__owur int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md,
-                              size_t len);
+__owur int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *out,
+                              size_t outlen);
+__owur int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *out,
+                             size_t outlen);
 
 __owur EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                             const char *properties);
index 423bed7983e05d671018463e715951d86dd92db5..19576c71909b00aa8bb75328a383e89d4329928e 100644 (file)
@@ -33,10 +33,12 @@ static OSSL_FUNC_digest_update_fn keccak_update;
 static OSSL_FUNC_digest_final_fn keccak_final;
 static OSSL_FUNC_digest_freectx_fn keccak_freectx;
 static OSSL_FUNC_digest_dupctx_fn keccak_dupctx;
+static OSSL_FUNC_digest_squeeze_fn shake_squeeze;
 static OSSL_FUNC_digest_set_ctx_params_fn shake_set_ctx_params;
 static OSSL_FUNC_digest_settable_ctx_params_fn shake_settable_ctx_params;
 static sha3_absorb_fn generic_sha3_absorb;
 static sha3_final_fn generic_sha3_final;
+static sha3_squeeze_fn generic_sha3_squeeze;
 
 #if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) && defined(KECCAK1600_ASM)
 /*
@@ -103,20 +105,37 @@ static int keccak_update(void *vctx, const unsigned char *inp, size_t len)
 }
 
 static int keccak_final(void *vctx, unsigned char *out, size_t *outl,
-                        size_t outsz)
+                        size_t outlen)
 {
     int ret = 1;
     KECCAK1600_CTX *ctx = vctx;
 
     if (!ossl_prov_is_running())
         return 0;
-    if (outsz > 0)
-        ret = ctx->meth.final(out, ctx);
+    if (outlen > 0)
+        ret = ctx->meth.final(ctx, out, ctx->md_size);
 
     *outl = ctx->md_size;
     return ret;
 }
 
+static int shake_squeeze(void *vctx, unsigned char *out, size_t *outl,
+                         size_t outlen)
+{
+    int ret = 1;
+    KECCAK1600_CTX *ctx = vctx;
+
+    if (!ossl_prov_is_running())
+        return 0;
+    if (ctx->meth.squeeze == NULL)
+        return 0;
+    if (outlen > 0)
+        ret = ctx->meth.squeeze(ctx, out, outlen);
+
+    *outl = outlen;
+    return ret;
+}
+
 /*-
  * Generic software version of the absorb() and final().
  */
@@ -127,15 +146,28 @@ static size_t generic_sha3_absorb(void *vctx, const void *inp, size_t len)
     return SHA3_absorb(ctx->A, inp, len, ctx->block_size);
 }
 
-static int generic_sha3_final(unsigned char *md, void *vctx)
+static int generic_sha3_final(void *vctx, unsigned char *out, size_t outlen)
 {
-    return ossl_sha3_final(md, (KECCAK1600_CTX *)vctx);
+    return ossl_sha3_final((KECCAK1600_CTX *)vctx, out, outlen);
+}
+
+static int generic_sha3_squeeze(void *vctx, unsigned char *out, size_t outlen)
+{
+    return ossl_sha3_squeeze((KECCAK1600_CTX *)vctx, out, outlen);
 }
 
 static PROV_SHA3_METHOD sha3_generic_md =
 {
     generic_sha3_absorb,
-    generic_sha3_final
+    generic_sha3_final,
+    NULL
+};
+
+static PROV_SHA3_METHOD shake_generic_md =
+{
+    generic_sha3_absorb,
+    generic_sha3_final,
+    generic_sha3_squeeze
 };
 
 #if defined(S390_SHA3)
@@ -156,59 +188,60 @@ static size_t s390x_sha3_absorb(void *vctx, const void *inp, size_t len)
     return rem;
 }
 
-static int s390x_sha3_final(unsigned char *md, void *vctx)
+static int s390x_sha3_final(void *vctx, unsigned char *out, size_t outlen)
 {
     KECCAK1600_CTX *ctx = vctx;
 
     if (!ossl_prov_is_running())
         return 0;
     s390x_klmd(ctx->buf, ctx->bufsz, NULL, 0, ctx->pad, ctx->A);
-    memcpy(md, ctx->A, ctx->md_size);
+    memcpy(out, ctx->A, outlen);
     return 1;
 }
 
-static int s390x_shake_final(unsigned char *md, void *vctx)
+static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen)
 {
     KECCAK1600_CTX *ctx = vctx;
 
     if (!ossl_prov_is_running())
         return 0;
-    s390x_klmd(ctx->buf, ctx->bufsz, md, ctx->md_size, ctx->pad, ctx->A);
+    s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, ctx->pad, ctx->A);
     return 1;
 }
 
-static int s390x_keccakc_final(unsigned char *md, void *vctx, int padding)
+static int s390x_keccakc_final(void *vctx, unsigned char *out, size_t outlen,
+                               int padding)
 {
     KECCAK1600_CTX *ctx = vctx;
     size_t bsz = ctx->block_size;
     size_t num = ctx->bufsz;
-    size_t needed = ctx->md_size;
+    size_t needed = outlen;
 
     if (!ossl_prov_is_running())
         return 0;
-    if (ctx->md_size == 0)
+    if (outlen == 0)
         return 1;
     memset(ctx->buf + num, 0, bsz - num);
     ctx->buf[num] = padding;
     ctx->buf[bsz - 1] |= 0x80;
     s390x_kimd(ctx->buf, bsz, ctx->pad, ctx->A);
     num = needed > bsz ? bsz : needed;
-    memcpy(md, ctx->A, num);
+    memcpy(out, ctx->A, num);
     needed -= num;
     if (needed > 0)
-        s390x_klmd(NULL, 0, md + bsz, needed, ctx->pad | S390X_KLMD_PS, ctx->A);
+        s390x_klmd(NULL, 0, out + bsz, needed, ctx->pad | S390X_KLMD_PS, ctx->A);
 
     return 1;
 }
 
-static int s390x_keccak_final(unsigned char *md, void *vctx)
+static int s390x_keccak_final(void *vctx, unsigned char *out, size_t outlen)
 {
-    return s390x_keccakc_final(md, vctx, 0x01);
+    return s390x_keccakc_final(vctx, out, outlen, 0x01);
 }
 
-static int s390x_kmac_final(unsigned char *md, void *vctx)
+static int s390x_kmac_final(void *vctx, unsigned char *out, size_t outlen)
 {
-    return s390x_keccakc_final(md, vctx, 0x04);
+    return s390x_keccakc_final(vctx, out, outlen, 0x04);
 }
 
 static PROV_SHA3_METHOD sha3_s390x_md =
@@ -220,7 +253,7 @@ static PROV_SHA3_METHOD sha3_s390x_md =
 static PROV_SHA3_METHOD keccak_s390x_md =
 {
     s390x_sha3_absorb,
-    s390x_keccak_final
+    s390x_keccak_final,
 };
 
 static PROV_SHA3_METHOD shake_s390x_md =
@@ -235,6 +268,14 @@ static PROV_SHA3_METHOD kmac_s390x_md =
     s390x_kmac_final
 };
 
+# define SHAKE_SET_MD(uname, typ)                                              \
+    if (S390_SHA3_CAPABLE(uname)) {                                            \
+        ctx->pad = S390X_##uname;                                              \
+        ctx->meth = typ##_s390x_md;                                            \
+    } else {                                                                   \
+        ctx->meth = shake_generic_md;                                          \
+    }
+
 # define SHA3_SET_MD(uname, typ)                                               \
     if (S390_SHA3_CAPABLE(uname)) {                                            \
         ctx->pad = S390X_##uname;                                              \
@@ -255,7 +296,7 @@ static PROV_SHA3_METHOD kmac_s390x_md =
 static sha3_absorb_fn armsha3_sha3_absorb;
 
 size_t SHA3_absorb_cext(uint64_t A[5][5], const unsigned char *inp, size_t len,
-                    size_t r);
+                        size_t r);
 /*-
  * Hardware-assisted ARMv8.2 SHA3 extension version of the absorb()
  */
@@ -271,6 +312,19 @@ static PROV_SHA3_METHOD sha3_ARMSHA3_md =
     armsha3_sha3_absorb,
     generic_sha3_final
 };
+static PROV_SHA3_METHOD shake_ARMSHA3_md =
+{
+    armsha3_sha3_absorb,
+    generic_sha3_final,
+    generic_sha3_squeeze
+};
+# define SHAKE_SET_MD(uname, typ)                                              \
+    if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) {                  \
+        ctx->meth = shake_ARMSHA3_md;                                          \
+    } else {                                                                   \
+        ctx->meth = shake_generic_md;                                          \
+    }
+
 # define SHA3_SET_MD(uname, typ)                                               \
     if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) {                  \
         ctx->meth = sha3_ARMSHA3_md;                                           \
@@ -286,6 +340,7 @@ static PROV_SHA3_METHOD sha3_ARMSHA3_md =
 #else
 # define SHA3_SET_MD(uname, typ) ctx->meth = sha3_generic_md;
 # define KMAC_SET_MD(bitlen) ctx->meth = sha3_generic_md;
+# define SHAKE_SET_MD(uname, typ) ctx->meth = shake_generic_md;
 #endif /* S390_SHA3 */
 
 #define SHA3_newctx(typ, uname, name, bitlen, pad)                             \
@@ -302,6 +357,20 @@ static void *name##_newctx(void *provctx)                                      \
     return ctx;                                                                \
 }
 
+#define SHAKE_newctx(typ, uname, name, bitlen, pad)                            \
+static OSSL_FUNC_digest_newctx_fn name##_newctx;                               \
+static void *name##_newctx(void *provctx)                                      \
+{                                                                              \
+    KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx))\
+                                                : NULL;                        \
+                                                                               \
+    if (ctx == NULL)                                                           \
+        return NULL;                                                           \
+    ossl_sha3_init(ctx, pad, bitlen);                                          \
+    SHAKE_SET_MD(uname, typ)                                                   \
+    return ctx;                                                                \
+}
+
 #define KMAC_newctx(uname, bitlen, pad)                                        \
 static OSSL_FUNC_digest_newctx_fn uname##_newctx;                              \
 static void *uname##_newctx(void *provctx)                                     \
@@ -333,6 +402,7 @@ const OSSL_DISPATCH ossl_##name##_functions[] = {                              \
 
 #define PROV_FUNC_SHAKE_DIGEST(name, bitlen, blksize, dgstsize, flags)         \
     PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags),      \
+    { OSSL_FUNC_DIGEST_SQUEEZE, (void (*)(void))shake_squeeze },               \
     { OSSL_FUNC_DIGEST_INIT, (void (*)(void))keccak_init_params },             \
     { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))shake_set_ctx_params }, \
     { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS,                                    \
@@ -398,7 +468,7 @@ static int shake_set_ctx_params(void *vctx, const OSSL_PARAM params[])
                           SHA3_FLAGS)
 
 #define IMPLEMENT_SHAKE_functions(bitlen)                                      \
-    SHA3_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, '\x1f')         \
+    SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, '\x1f')        \
     PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen,                             \
                           SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen),         \
                           SHAKE_FLAGS)
index 1784a41d8d22c13f970bd7336d32bbacd1155fb2..10f29b19cfb2981e5c01b928636bcc07a74a1610 100644 (file)
@@ -62,7 +62,8 @@ IF[{- !$disabled{tests} -}]
           bio_readbuffer_test user_property_test pkcs7_test upcallstest \
           provfetchtest prov_config_test rand_test ca_internals_test \
           bio_tfo_test membio_test bio_dgram_test list_test fips_version_test \
-          x509_test hpke_test pairwise_fail_test nodefltctxtest
+          x509_test hpke_test pairwise_fail_test nodefltctxtest \
+          evp_xof_test
 
   IF[{- !$disabled{'rpk'} -}]
     PROGRAMS{noinst}=rpktest
@@ -558,6 +559,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[evp_kdf_test]=../include ../apps/include
   DEPEND[evp_kdf_test]=../libcrypto libtestutil.a
 
+  SOURCE[evp_xof_test]=evp_xof_test.c
+  INCLUDE[evp_xof_test]=../include ../apps/include
+  DEPEND[evp_xof_test]=../libcrypto libtestutil.a
+
   SOURCE[evp_pkey_dparams_test]=evp_pkey_dparams_test.c
   INCLUDE[evp_pkey_dparams_test]=../include ../apps/include
   DEPEND[evp_pkey_dparams_test]=../libcrypto libtestutil.a
diff --git a/test/evp_xof_test.c b/test/evp_xof_test.c
new file mode 100644 (file)
index 0000000..eeff866
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/core_names.h>
+#include "testutil.h"
+#include "internal/nelem.h"
+
+static const unsigned char shake256_input[] = {
+    0x8d, 0x80, 0x01, 0xe2, 0xc0, 0x96, 0xf1, 0xb8,
+    0x8e, 0x7c, 0x92, 0x24, 0xa0, 0x86, 0xef, 0xd4,
+    0x79, 0x7f, 0xbf, 0x74, 0xa8, 0x03, 0x3a, 0x2d,
+    0x42, 0x2a, 0x2b, 0x6b, 0x8f, 0x67, 0x47, 0xe4
+};
+
+/*
+ * This KAT output is 250 bytes, which is more than
+ * the SHAKE256 block size (136 bytes).
+ */
+static const unsigned char shake256_output[] = {
+    0x2e, 0x97, 0x5f, 0x6a, 0x8a, 0x14, 0xf0, 0x70,
+    0x4d, 0x51, 0xb1, 0x36, 0x67, 0xd8, 0x19, 0x5c,
+    0x21, 0x9f, 0x71, 0xe6, 0x34, 0x56, 0x96, 0xc4,
+    0x9f, 0xa4, 0xb9, 0xd0, 0x8e, 0x92, 0x25, 0xd3,
+    0xd3, 0x93, 0x93, 0x42, 0x51, 0x52, 0xc9, 0x7e,
+    0x71, 0xdd, 0x24, 0x60, 0x1c, 0x11, 0xab, 0xcf,
+    0xa0, 0xf1, 0x2f, 0x53, 0xc6, 0x80, 0xbd, 0x3a,
+    0xe7, 0x57, 0xb8, 0x13, 0x4a, 0x9c, 0x10, 0xd4,
+    0x29, 0x61, 0x58, 0x69, 0x21, 0x7f, 0xdd, 0x58,
+    0x85, 0xc4, 0xdb, 0x17, 0x49, 0x85, 0x70, 0x3a,
+    0x6d, 0x6d, 0xe9, 0x4a, 0x66, 0x7e, 0xac, 0x30,
+    0x23, 0x44, 0x3a, 0x83, 0x37, 0xae, 0x1b, 0xc6,
+    0x01, 0xb7, 0x6d, 0x7d, 0x38, 0xec, 0x3c, 0x34,
+    0x46, 0x31, 0x05, 0xf0, 0xd3, 0x94, 0x9d, 0x78,
+    0xe5, 0x62, 0xa0, 0x39, 0xe4, 0x46, 0x95, 0x48,
+    0xb6, 0x09, 0x39, 0x5d, 0xe5, 0xa4, 0xfd, 0x43,
+    0xc4, 0x6c, 0xa9, 0xfd, 0x6e, 0xe2, 0x9a, 0xda,
+    0x5e, 0xfc, 0x07, 0xd8, 0x4d, 0x55, 0x32, 0x49,
+    0x45, 0x0d, 0xab, 0x4a, 0x49, 0xc4, 0x83, 0xde,
+    0xd2, 0x50, 0xc9, 0x33, 0x8f, 0x85, 0xcd, 0x93,
+    0x7a, 0xe6, 0x6b, 0xb4, 0x36, 0xf3, 0xb4, 0x02,
+    0x6e, 0x85, 0x9f, 0xda, 0x1c, 0xa5, 0x71, 0x43,
+    0x2f, 0x3b, 0xfc, 0x09, 0xe7, 0xc0, 0x3c, 0xa4,
+    0xd1, 0x83, 0xb7, 0x41, 0x11, 0x1c, 0xa0, 0x48,
+    0x3d, 0x0e, 0xda, 0xbc, 0x03, 0xfe, 0xb2, 0x3b,
+    0x17, 0xee, 0x48, 0xe8, 0x44, 0xba, 0x24, 0x08,
+    0xd9, 0xdc, 0xfd, 0x01, 0x39, 0xd2, 0xe8, 0xc7,
+    0x31, 0x01, 0x25, 0xae, 0xe8, 0x01, 0xc6, 0x1a,
+    0xb7, 0x90, 0x0d, 0x1e, 0xfc, 0x47, 0xc0, 0x78,
+    0x28, 0x17, 0x66, 0xf3, 0x61, 0xc5, 0xe6, 0x11,
+    0x13, 0x46, 0x23, 0x5e, 0x1d, 0xc3, 0x83, 0x25,
+    0x66, 0x6c
+};
+
+static const unsigned char shake256_largemsg_input[] = {
+    0xb2, 0xd2, 0x38, 0x65, 0xaf, 0x8f, 0x25, 0x6e,
+    0x64, 0x40, 0xe2, 0x0d, 0x49, 0x8e, 0x3e, 0x64,
+    0x46, 0xd2, 0x03, 0xa4, 0x19, 0xe3, 0x7b, 0x80,
+    0xf7, 0x2b, 0x32, 0xe2, 0x76, 0x01, 0xfe, 0xdd,
+    0xaa, 0x33, 0x3d, 0xe4, 0x8e, 0xe1, 0x5e, 0x39,
+    0xa6, 0x92, 0xa3, 0xa7, 0xe3, 0x81, 0x24, 0x74,
+    0xc7, 0x38, 0x18, 0x92, 0xc9, 0x60, 0x50, 0x15,
+    0xfb, 0xd8, 0x04, 0xea, 0xea, 0x04, 0xd2, 0xc5,
+    0xc6, 0x68, 0x04, 0x5b, 0xc3, 0x75, 0x12, 0xd2,
+    0xbe, 0xa2, 0x67, 0x75, 0x24, 0xbf, 0x68, 0xad,
+    0x10, 0x86, 0xb3, 0x2c, 0xb3, 0x74, 0xa4, 0x6c,
+    0xf9, 0xd7, 0x1e, 0x58, 0x69, 0x27, 0x88, 0x49,
+    0x4e, 0x99, 0x15, 0x33, 0x14, 0xf2, 0x49, 0x21,
+    0xf4, 0x99, 0xb9, 0xde, 0xd4, 0xf1, 0x12, 0xf5,
+    0x68, 0xe5, 0x5c, 0xdc, 0x9e, 0xc5, 0x80, 0x6d,
+    0x39, 0x50, 0x08, 0x95, 0xbb, 0x12, 0x27, 0x50,
+    0x89, 0xf0, 0xf9, 0xd5, 0x4a, 0x01, 0x0b, 0x0d,
+    0x90, 0x9f, 0x1e, 0x4a, 0xba, 0xbe, 0x28, 0x36,
+    0x19, 0x7d, 0x9c, 0x0a, 0x51, 0xfb, 0xeb, 0x00,
+    0x02, 0x6c, 0x4b, 0x0a, 0xa8, 0x6c, 0xb7, 0xc4,
+    0xc0, 0x92, 0x37, 0xa7, 0x2d, 0x49, 0x61, 0x80,
+    0xd9, 0xdb, 0x20, 0x21, 0x9f, 0xcf, 0xb4, 0x57,
+    0x69, 0x75, 0xfa, 0x1c, 0x95, 0xbf, 0xee, 0x0d,
+    0x9e, 0x52, 0x6e, 0x1e, 0xf8, 0xdd, 0x41, 0x8c,
+    0x3b, 0xaa, 0x57, 0x13, 0x84, 0x73, 0x52, 0x62,
+    0x18, 0x76, 0x46, 0xcc, 0x4b, 0xcb, 0xbd, 0x40,
+    0xa1, 0xf6, 0xff, 0x7b, 0x32, 0xb9, 0x90, 0x7c,
+    0x53, 0x2c, 0xf9, 0x38, 0x72, 0x0f, 0xcb, 0x90,
+    0x42, 0x5e, 0xe2, 0x80, 0x19, 0x26, 0xe7, 0x99,
+    0x96, 0x98, 0x18, 0xb1, 0x86, 0x5b, 0x4c, 0xd9,
+    0x08, 0x27, 0x31, 0x8f, 0xf0, 0x90, 0xd9, 0x35,
+    0x6a, 0x1f, 0x75, 0xc2, 0xe0, 0xa7, 0x60, 0xb8,
+    0x1d, 0xd6, 0x5f, 0x56, 0xb2, 0x0b, 0x27, 0x0e,
+    0x98, 0x67, 0x1f, 0x39, 0x18, 0x27, 0x68, 0x0a,
+    0xe8, 0x31, 0x1b, 0xc0, 0x97, 0xec, 0xd1, 0x20,
+    0x2a, 0x55, 0x69, 0x23, 0x08, 0x50, 0x05, 0xec,
+    0x13, 0x3b, 0x56, 0xfc, 0x18, 0xc9, 0x1a, 0xa9,
+    0x69, 0x0e, 0xe2, 0xcc, 0xc8, 0xd6, 0x19, 0xbb,
+    0x87, 0x3b, 0x42, 0x77, 0xee, 0x77, 0x81, 0x26,
+    0xdd, 0xf6, 0x5d, 0xc3, 0xb2, 0xb0, 0xc4, 0x14,
+    0x6d, 0xb5, 0x4f, 0xdc, 0x13, 0x09, 0xc8, 0x53,
+    0x50, 0xb3, 0xea, 0xd3, 0x5f, 0x11, 0x67, 0xd4,
+    0x2f, 0x6e, 0x30, 0x1a, 0xbe, 0xd6, 0xf0, 0x2d,
+    0xc9, 0x29, 0xd9, 0x0a, 0xa8, 0x6f, 0xa4, 0x18,
+    0x74, 0x6b, 0xd3, 0x5d, 0x6a, 0x73, 0x3a, 0xf2,
+    0x94, 0x7f, 0xbd, 0xb4, 0xa6, 0x7f, 0x5b, 0x3d,
+    0x26, 0xf2, 0x6c, 0x13, 0xcf, 0xb4, 0x26, 0x1e,
+    0x38, 0x17, 0x66, 0x60, 0xb1, 0x36, 0xae, 0xe0,
+    0x6d, 0x86, 0x69, 0xe7, 0xe7, 0xae, 0x77, 0x6f,
+    0x7e, 0x99, 0xe5, 0xd9, 0x62, 0xc9, 0xfc, 0xde,
+    0xb4, 0xee, 0x7e, 0xc8, 0xe9, 0xb7, 0x2c, 0xe2,
+    0x70, 0xe8, 0x8b, 0x2d, 0x94, 0xad, 0xe8, 0x54,
+    0xa3, 0x2d, 0x9a, 0xe2, 0x50, 0x63, 0x87, 0xb3,
+    0x56, 0x29, 0xea, 0xa8, 0x5e, 0x96, 0x53, 0x9f,
+    0x23, 0x8a, 0xef, 0xa3, 0xd4, 0x87, 0x09, 0x5f,
+    0xba, 0xc3, 0xd1, 0xd9, 0x1a, 0x7b, 0x5c, 0x5d,
+    0x5d, 0x89, 0xed, 0xb6, 0x6e, 0x39, 0x73, 0xa5,
+    0x64, 0x59, 0x52, 0x8b, 0x61, 0x8f, 0x66, 0x69,
+    0xb9, 0xf0, 0x45, 0x0a, 0x57, 0xcd, 0xc5, 0x7f,
+    0x5d, 0xd0, 0xbf, 0xcc, 0x0b, 0x48, 0x12, 0xe1,
+    0xe2, 0xc2, 0xea, 0xcc, 0x09, 0xd9, 0x42, 0x2c,
+    0xef, 0x4f, 0xa7, 0xe9, 0x32, 0x5c, 0x3f, 0x22,
+    0xc0, 0x45, 0x0b, 0x67, 0x3c, 0x31, 0x69, 0x29,
+    0xa3, 0x39, 0xdd, 0x6e, 0x2f, 0xbe, 0x10, 0xc9,
+    0x7b, 0xff, 0x19, 0x8a, 0xe9, 0xea, 0xfc, 0x32,
+    0x41, 0x33, 0x70, 0x2a, 0x9a, 0xa4, 0xe6, 0xb4,
+    0x7e, 0xb4, 0xc6, 0x21, 0x49, 0x5a, 0xfc, 0x45,
+    0xd2, 0x23, 0xb3, 0x28, 0x4d, 0x83, 0x60, 0xfe,
+    0x70, 0x68, 0x03, 0x59, 0xd5, 0x15, 0xaa, 0x9e,
+    0xa0, 0x2e, 0x36, 0xb5, 0x61, 0x0f, 0x61, 0x05,
+    0x3c, 0x62, 0x00, 0xa0, 0x47, 0xf1, 0x86, 0xba,
+    0x33, 0xb8, 0xca, 0x60, 0x2f, 0x3f, 0x0a, 0x67,
+    0x09, 0x27, 0x2f, 0xa2, 0x96, 0x02, 0x52, 0x58,
+    0x55, 0x68, 0x80, 0xf4, 0x4f, 0x47, 0xba, 0xff,
+    0x41, 0x7a, 0x40, 0x4c, 0xfd, 0x9d, 0x10, 0x72,
+    0x0e, 0x20, 0xa9, 0x7f, 0x9b, 0x9b, 0x14, 0xeb,
+    0x8e, 0x61, 0x25, 0xcb, 0xf4, 0x58, 0xff, 0x47,
+    0xa7, 0x08, 0xd6, 0x4e, 0x2b, 0xf1, 0xf9, 0x89,
+    0xd7, 0x22, 0x0f, 0x8d, 0x35, 0x07, 0xa0, 0x54,
+    0xab, 0x83, 0xd8, 0xee, 0x5a, 0x3e, 0x88, 0x74,
+    0x46, 0x41, 0x6e, 0x3e, 0xb7, 0xc0, 0xb6, 0x55,
+    0xe0, 0x36, 0xc0, 0x2b, 0xbf, 0xb8, 0x24, 0x8a,
+    0x44, 0x82, 0xf4, 0xcb, 0xb5, 0xd7, 0x41, 0x48,
+    0x51, 0x08, 0xe0, 0x14, 0x34, 0xd2, 0x6d, 0xe9,
+    0x7a, 0xec, 0x91, 0x61, 0xa7, 0xe1, 0x81, 0x69,
+    0x47, 0x1c, 0xc7, 0xf3
+};
+
+static const unsigned char shake256_largemsg_output[] = {
+    0x64, 0xea, 0x24, 0x6a, 0xab, 0x80, 0x37, 0x9e,
+    0x08, 0xe2, 0x19, 0x9e, 0x09, 0x69, 0xe2, 0xee,
+    0x1a, 0x5d, 0xd1, 0x68, 0x68, 0xec, 0x8d, 0x42,
+    0xd0, 0xf8, 0xb8, 0x44, 0x74, 0x54, 0x87, 0x3e,
+};
+
+static EVP_MD_CTX *shake_setup(const char *name)
+{
+    EVP_MD_CTX *ctx = NULL;
+    EVP_MD *md = NULL;
+
+    if (!TEST_ptr(md = EVP_MD_fetch(NULL, name, NULL)))
+        return NULL;
+
+    if (!TEST_ptr(ctx = EVP_MD_CTX_new()))
+        goto err;
+    if (!TEST_true(EVP_DigestInit_ex2(ctx, md, NULL)))
+        goto err;
+    EVP_MD_free(md);
+    return ctx;
+err:
+    EVP_MD_free(md);
+    EVP_MD_CTX_free(ctx);
+    return NULL;
+}
+
+static int shake_kat_test(void)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char out[sizeof(shake256_output)];
+
+    if (!TEST_ptr(ctx = shake_setup("SHAKE256")))
+        return 0;
+    if (!TEST_true(EVP_DigestUpdate(ctx, shake256_input,
+                                    sizeof(shake256_input)))
+        || !TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out)))
+        || !TEST_mem_eq(out, sizeof(out),
+                        shake256_output,sizeof(shake256_output))
+        /* Test that a second call to EVP_DigestFinalXOF fails */
+        || !TEST_false(EVP_DigestFinalXOF(ctx, out, sizeof(out)))
+        /* Test that a call to EVP_DigestSqueeze fails */
+        || !TEST_false(EVP_DigestSqueeze(ctx, out, sizeof(out))))
+        goto err;
+    ret = 1;
+err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+static int shake_kat_digestfinal_test(void)
+{
+    int ret = 0;
+    unsigned int digest_length = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char out[sizeof(shake256_output)];
+
+    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)
+        || !TEST_false(EVP_DigestFinalXOF(ctx, out, sizeof(out))))
+        goto err;
+    ret = 1;
+err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+/*
+ * Test that EVP_DigestFinal() returns the output length
+ * set by the OSSL_DIGEST_PARAM_XOFLEN param.
+ */
+static int shake_kat_digestfinal_xoflen_test(void)
+{
+    int ret = 0;
+    unsigned int digest_length = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char out[sizeof(shake256_output)];
+    OSSL_PARAM params[2];
+    size_t sz = 12;
+
+    if (!TEST_ptr(ctx = shake_setup("SHAKE256")))
+        return 0;
+
+    memset(out, 0, sizeof(out));
+    params[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &sz);
+    params[1] = OSSL_PARAM_construct_end();
+
+    if (!TEST_int_eq(EVP_MD_CTX_set_params(ctx, params), 1)
+        || !TEST_true(EVP_DigestUpdate(ctx, shake256_input,
+                                       sizeof(shake256_input)))
+        || !TEST_true(EVP_DigestFinal(ctx, out, &digest_length))
+        || !TEST_uint_eq(digest_length, (unsigned int)sz)
+        || !TEST_mem_eq(out, digest_length,
+                        shake256_output, digest_length)
+        || !TEST_uchar_eq(out[digest_length], 0))
+        goto err;
+    ret = 1;
+err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+/*
+ * Test that multiple absorb calls gives the expected result.
+ * This is a nested test that uses multiple strides for the input.
+ */
+static int shake_absorb_test(void)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char out[sizeof(shake256_largemsg_output)];
+    size_t total = sizeof(shake256_largemsg_input);
+    size_t i, stride, sz;
+
+    if (!TEST_ptr(ctx = shake_setup("SHAKE256")))
+        return 0;
+
+    for (stride = 1; stride < total; ++stride) {
+        sz = 0;
+        for (i = 0; i < total; i += sz) {
+            sz += stride;
+            if ((i + sz) > total)
+                sz = total - i;
+            if (!TEST_true(EVP_DigestUpdate(ctx, shake256_largemsg_input + i,
+                                            sz)))
+                goto err;
+        }
+        if (!TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out)))
+            || !TEST_mem_eq(out, sizeof(out),
+                            shake256_largemsg_output,
+                            sizeof(shake256_largemsg_output)))
+            goto err;
+        if (!TEST_true(EVP_DigestInit_ex2(ctx, NULL, NULL)))
+            goto err;
+    }
+    ret = 1;
+err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+/*
+ * Table containing the size of the output to squeeze for the
+ * initially call, followed by a size for each subsequent call.
+ */
+static const struct {
+    size_t startsz, incsz;
+} stride_tests[] = {
+    { 1, 1 },
+    { 1, 136 },
+    { 1, 136/2 },
+    { 1, 136/2-1 },
+    { 1, 136/2+1 },
+    { 1, 136*3 },
+    { 8, 8 },
+    { 9, 9 },
+    { 10, 10 },
+    { 136/2 - 1, 136 },
+    { 136/2 - 1, 136-1 },
+    { 136/2 - 1, 136+1 },
+    { 136/2, 136 },
+    { 136/2, 136-1 },
+    { 136/2, 136+1 },
+    { 136/2 + 1, 136 },
+    { 136/2 + 1, 136-1 },
+    { 136/2 + 1, 136+1 },
+    { 136, 2 },
+    { 136, 136 },
+    { 136-1, 136 },
+    { 136-1, 136-1 },
+    { 136-1, 136+1 },
+    { 136+1, 136 },
+    { 136+1, 136-1 },
+    { 136+1, 136+1 },
+    { 136*3, 136 },
+    { 136*3, 136 + 1 },
+    { 136*3, 136 - 1 },
+    { 136*3, 136/2 },
+    { 136*3, 136/2 + 1 },
+    { 136*3, 136/2 - 1 },
+};
+
+/*
+ * Helper to do multiple squeezes of output data using SHAKE256.
+ * tst is an index into the stride_tests[] containing an initial starting
+ * output length, followed by a second output length to use for all remaining
+ * squeezes. expected_outlen contains the total number of bytes to squeeze.
+ * in and inlen represent the input to absorb. expected_out and expected_outlen
+ * represent the expected output.
+ */
+static int do_shake_squeeze_test(int tst,
+                                 const unsigned char *in, size_t inlen,
+                                 const unsigned char *expected_out,
+                                 size_t expected_outlen)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char *out = NULL;
+    size_t i = 0, sz = stride_tests[tst].startsz;
+
+    if (!TEST_ptr(ctx = shake_setup("SHAKE256")))
+        return 0;
+    if (!TEST_ptr(out = OPENSSL_malloc(expected_outlen)))
+        goto err;
+    if (!TEST_true(EVP_DigestUpdate(ctx, in, inlen)))
+        goto err;
+
+    while (i < expected_outlen) {
+        if ((i + sz) > expected_outlen)
+            sz = expected_outlen - i;
+        if (!TEST_true(EVP_DigestSqueeze(ctx, out + i, sz)))
+            goto err;
+        i += sz;
+        sz = stride_tests[tst].incsz;
+    }
+    if (!TEST_mem_eq(out, expected_outlen, expected_out, expected_outlen))
+        goto err;
+    ret = 1;
+err:
+    OPENSSL_free(out);
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+static int shake_squeeze_kat_test(int tst)
+{
+    return do_shake_squeeze_test(tst, shake256_input, sizeof(shake256_input),
+                                 shake256_output, sizeof(shake256_output));
+}
+
+/*
+ * Generate some random input to absorb, and then
+ * squeeze it out in one operation to get a expected
+ * output. Use this to test that multiple squeeze calls
+ * on the same input gives the same output.
+ */
+static int shake_squeeze_large_test(int tst)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char msg[16];
+    unsigned char out[2000];
+
+    if (!TEST_int_gt(RAND_bytes(msg, sizeof(msg)), 0)
+        || !TEST_ptr(ctx = shake_setup("SHAKE256"))
+        || !TEST_true(EVP_DigestUpdate(ctx, msg, sizeof(msg)))
+        || !TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out))))
+        goto err;
+
+    ret = do_shake_squeeze_test(tst, msg, sizeof(msg), out, sizeof(out));
+err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+static const size_t dupoffset_tests[] = {
+    1, 135, 136, 137, 136*3-1, 136*3, 136*3+1
+};
+
+/* Helper function to test that EVP_MD_CTX_dup() copies the internal state */
+static int do_shake_squeeze_dup_test(int tst, const char *alg,
+                                     const unsigned char *in, size_t inlen,
+                                     const unsigned char *expected_out,
+                                     size_t expected_outlen)
+{
+    int ret = 0;
+    EVP_MD_CTX *cur, *ctx = NULL, *dupctx = NULL;
+    unsigned char *out = NULL;
+    size_t i = 0, sz = 10;
+    size_t dupoffset = dupoffset_tests[tst];
+
+    if (!TEST_ptr(ctx = shake_setup(alg)))
+        return 0;
+    cur = ctx;
+    if (!TEST_ptr(out = OPENSSL_malloc(expected_outlen)))
+        goto err;
+    if (!TEST_true(EVP_DigestUpdate(ctx, in, inlen)))
+        goto err;
+
+    while (i < expected_outlen) {
+        if ((i + sz) > expected_outlen)
+            sz = expected_outlen - i;
+        if (!TEST_true(EVP_DigestSqueeze(cur, out + i, sz)))
+            goto err;
+        i += sz;
+        /* At a certain offset we swap to a new ctx that copies the state */
+        if (dupctx == NULL && i >= dupoffset) {
+            if (!TEST_ptr(dupctx = EVP_MD_CTX_dup(ctx)))
+                goto err;
+            cur = dupctx;
+        }
+    }
+    if (!TEST_mem_eq(out, expected_outlen, expected_out, expected_outlen))
+        goto err;
+    ret = 1;
+err:
+    OPENSSL_free(out);
+    EVP_MD_CTX_free(ctx);
+    EVP_MD_CTX_free(dupctx);
+    return ret;
+}
+
+/* Test that the internal state can be copied */
+static int shake_squeeze_dup_test(int tst)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char msg[16];
+    unsigned char out[1000];
+    const char *alg = "SHAKE128";
+
+    if (!TEST_int_gt(RAND_bytes(msg, sizeof(msg)), 0)
+        || !TEST_ptr(ctx = shake_setup(alg))
+        || !TEST_true(EVP_DigestUpdate(ctx, msg, sizeof(msg)))
+        || !TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out))))
+        goto err;
+
+    ret = do_shake_squeeze_dup_test(tst, alg, msg, sizeof(msg),
+                                    out, sizeof(out));
+err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+int setup_tests(void)
+{
+    ADD_TEST(shake_kat_test);
+    ADD_TEST(shake_kat_digestfinal_test);
+    ADD_TEST(shake_kat_digestfinal_xoflen_test);
+    ADD_TEST(shake_absorb_test);
+    ADD_ALL_TESTS(shake_squeeze_kat_test, OSSL_NELEM(stride_tests));
+    ADD_ALL_TESTS(shake_squeeze_large_test, OSSL_NELEM(stride_tests));
+    ADD_ALL_TESTS(shake_squeeze_dup_test, OSSL_NELEM(dupoffset_tests));
+    return 1;
+}
diff --git a/test/recipes/30-test_evp_xof.t b/test/recipes/30-test_evp_xof.t
new file mode 100644 (file)
index 0000000..c3dd606
--- /dev/null
@@ -0,0 +1,12 @@
+#! /usr/bin/env perl
+# Copyright 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
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+use OpenSSL::Test::Simple;
+
+simple_test("test_evp_xof", "evp_xof_test");
index a16f93db47ee4f2c66071da29249242036145903..b64b0ddc5cb03ec4bdb7687c5fc8571ceeac5c8b 100644 (file)
@@ -5536,3 +5536,4 @@ X509_STORE_CTX_set_get_crl              ? 3_2_0   EXIST::FUNCTION:
 X509_STORE_CTX_set_current_reasons      ?      3_2_0   EXIST::FUNCTION:
 OSSL_STORE_delete                       ?      3_2_0   EXIST::FUNCTION:
 BIO_ADDR_copy                           ?      3_2_0   EXIST::FUNCTION:SOCK
+EVP_DigestSqueeze                       ?      3_2_0   EXIST::FUNCTION: