Skip to content

Commit 5366490

Browse files
slontist8m
authored andcommitted
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 #7921 Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from #21511)
1 parent 9257a89 commit 5366490

24 files changed

Lines changed: 938 additions & 108 deletions

crypto/evp/digest.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *isize)
502502
return ret;
503503
}
504504

505+
/* This is a one shot operation */
505506
int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
506507
{
507508
int ret = 0;
@@ -526,10 +527,15 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
526527
return 0;
527528
}
528529

530+
/*
531+
* For backward compatibility we pass the XOFLEN via a param here so that
532+
* older providers can use the supplied value. Ideally we should have just
533+
* used the size passed into ctx->digest->dfinal().
534+
*/
529535
params[i++] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &size);
530536
params[i++] = OSSL_PARAM_construct_end();
531537

532-
if (EVP_MD_CTX_set_params(ctx, params) > 0)
538+
if (EVP_MD_CTX_set_params(ctx, params) >= 0)
533539
ret = ctx->digest->dfinal(ctx->algctx, md, &size, size);
534540

535541
ctx->flags |= EVP_MD_CTX_FLAG_FINALISED;
@@ -553,6 +559,27 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
553559
return ret;
554560
}
555561

562+
/* EVP_DigestSqueeze() can be called multiple times */
563+
int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *md, size_t size)
564+
{
565+
if (ctx->digest == NULL) {
566+
ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM);
567+
return 0;
568+
}
569+
570+
if (ctx->digest->prov == NULL) {
571+
ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_OPERATION);
572+
return 0;
573+
}
574+
575+
if (ctx->digest->dsqueeze == NULL) {
576+
ERR_raise(ERR_LIB_EVP, EVP_R_METHOD_NOT_SUPPORTED);
577+
return 0;
578+
}
579+
580+
return ctx->digest->dsqueeze(ctx->algctx, md, &size, size);
581+
}
582+
556583
EVP_MD_CTX *EVP_MD_CTX_dup(const EVP_MD_CTX *in)
557584
{
558585
EVP_MD_CTX *out = EVP_MD_CTX_new();
@@ -1032,6 +1059,12 @@ static void *evp_md_from_algorithm(int name_id,
10321059
fncnt++;
10331060
}
10341061
break;
1062+
case OSSL_FUNC_DIGEST_SQUEEZE:
1063+
if (md->dsqueeze == NULL) {
1064+
md->dsqueeze = OSSL_FUNC_digest_squeeze(fns);
1065+
fncnt++;
1066+
}
1067+
break;
10351068
case OSSL_FUNC_DIGEST_DIGEST:
10361069
if (md->digest == NULL)
10371070
md->digest = OSSL_FUNC_digest_digest(fns);
@@ -1075,7 +1108,7 @@ static void *evp_md_from_algorithm(int name_id,
10751108
break;
10761109
}
10771110
}
1078-
if ((fncnt != 0 && fncnt != 5)
1111+
if ((fncnt != 0 && fncnt != 5 && fncnt != 6)
10791112
|| (fncnt == 0 && md->digest == NULL)) {
10801113
/*
10811114
* In order to be a consistent set of functions we either need the

crypto/evp/legacy_sha.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ static int nm##_update(EVP_MD_CTX *ctx, const void *data, size_t count) \
3737
} \
3838
static int nm##_final(EVP_MD_CTX *ctx, unsigned char *md) \
3939
{ \
40-
return fn##_final(md, EVP_MD_CTX_get0_md_data(ctx)); \
40+
KECCAK1600_CTX *kctx = EVP_MD_CTX_get0_md_data(ctx); \
41+
return fn##_final(kctx, md, kctx->md_size); \
4142
}
4243
#define IMPLEMENT_LEGACY_EVP_MD_METH_SHAKE(nm, fn, tag) \
4344
static int nm##_init(EVP_MD_CTX *ctx) \

crypto/sha/asm/keccak1600-armv4.pl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,8 @@ sub Round {
966966
stmdb sp!,{r6-r9}
967967
968968
mov r14,$A_flat
969+
cmp r4, #0 @ r4 = 'next' argument
970+
bne .Lnext_block
969971
b .Loop_squeeze
970972
971973
.align 4
@@ -1037,7 +1039,7 @@ sub Round {
10371039
10381040
subs $bsz,$bsz,#8 @ bsz -= 8
10391041
bhi .Loop_squeeze
1040-
1042+
.Lnext_block:
10411043
mov r0,r14 @ original $A_flat
10421044
10431045
bl KeccakF1600

crypto/sha/asm/keccak1600-armv8.pl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@
483483
mov $out,x1
484484
mov $len,x2
485485
mov $bsz,x3
486+
cmp x4, #0 // x4 = 'next' argument
487+
bne .Lnext_block
486488
487489
.Loop_squeeze:
488490
ldr x4,[x0],#8
@@ -497,7 +499,7 @@
497499
498500
subs x3,x3,#8
499501
bhi .Loop_squeeze
500-
502+
.Lnext_block:
501503
mov x0,$A_flat
502504
bl KeccakF1600
503505
mov x0,$A_flat

crypto/sha/asm/keccak1600-ppc64.pl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,8 @@
668668
subi $out,r4,1 ; prepare for stbu
669669
mr $len,r5
670670
mr $bsz,r6
671+
${UCMP}i r7,1 ; r7 = 'next' argument
672+
blt .Lnext_block
671673
b .Loop_squeeze
672674
673675
.align 4
@@ -698,6 +700,7 @@
698700
subic. r6,r6,8
699701
bgt .Loop_squeeze
700702
703+
.Lnext_block:
701704
mr r3,$A_flat
702705
bl KeccakF1600
703706
subi r3,$A_flat,8 ; prepare for ldu

crypto/sha/asm/keccak1600-x86_64.pl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -503,12 +503,12 @@
503503
.size SHA3_absorb,.-SHA3_absorb
504504
___
505505
}
506-
{ my ($A_flat,$out,$len,$bsz) = ("%rdi","%rsi","%rdx","%rcx");
506+
{ my ($A_flat,$out,$len,$bsz,$next) = ("%rdi","%rsi","%rdx","%rcx","%r8");
507507
($out,$len,$bsz) = ("%r12","%r13","%r14");
508508

509509
$code.=<<___;
510510
.globl SHA3_squeeze
511-
.type SHA3_squeeze,\@function,4
511+
.type SHA3_squeeze,\@function,5
512512
.align 32
513513
SHA3_squeeze:
514514
.cfi_startproc
@@ -520,34 +520,36 @@
520520
.cfi_push %r14
521521
522522
shr \$3,%rcx
523-
mov $A_flat,%r8
523+
mov $A_flat,%r9
524524
mov %rsi,$out
525525
mov %rdx,$len
526526
mov %rcx,$bsz
527+
bt \$0,$next
528+
jc .Lnext_block
527529
jmp .Loop_squeeze
528530
529531
.align 32
530532
.Loop_squeeze:
531533
cmp \$8,$len
532534
jb .Ltail_squeeze
533535
534-
mov (%r8),%rax
535-
lea 8(%r8),%r8
536+
mov (%r9),%rax
537+
lea 8(%r9),%r9
536538
mov %rax,($out)
537539
lea 8($out),$out
538540
sub \$8,$len # len -= 8
539541
jz .Ldone_squeeze
540542
541543
sub \$1,%rcx # bsz--
542544
jnz .Loop_squeeze
543-
545+
.Lnext_block:
544546
call KeccakF1600
545-
mov $A_flat,%r8
547+
mov $A_flat,%r9
546548
mov $bsz,%rcx
547549
jmp .Loop_squeeze
548550
549551
.Ltail_squeeze:
550-
mov %r8, %rsi
552+
mov %r9, %rsi
551553
mov $out,%rdi
552554
mov $len,%rcx
553555
.byte 0xf3,0xa4 # rep movsb

crypto/sha/keccak1600.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len,
1515
size_t r);
16-
void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r);
16+
void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next);
1717

1818
#if !defined(KECCAK1600_ASM) || !defined(SELFTEST)
1919

@@ -1090,17 +1090,26 @@ size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len,
10901090
}
10911091

10921092
/*
1093-
* sha3_squeeze is called once at the end to generate |out| hash value
1094-
* of |len| bytes.
1093+
* SHA3_squeeze may be called after SHA3_absorb to generate |out| hash value of
1094+
* |len| bytes.
1095+
* If multiple SHA3_squeeze calls are required the output length |len| must be a
1096+
* multiple of the blocksize, with |next| being 0 on the first call and 1 on
1097+
* subsequent calls. It is the callers responsibility to buffer the results.
1098+
* When only a single call to SHA3_squeeze is required, |len| can be any size
1099+
* and |next| must be 0.
10951100
*/
1096-
void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r)
1101+
void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r,
1102+
int next)
10971103
{
10981104
uint64_t *A_flat = (uint64_t *)A;
10991105
size_t i, w = r / 8;
11001106

11011107
assert(r < (25 * sizeof(A[0][0])) && (r % 8) == 0);
11021108

11031109
while (len != 0) {
1110+
if (next)
1111+
KeccakF1600(A);
1112+
next = 1;
11041113
for (i = 0; i < w && len != 0; i++) {
11051114
uint64_t Ai = BitDeinterleave(A_flat[i]);
11061115

@@ -1123,8 +1132,6 @@ void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r)
11231132
out += 8;
11241133
len -= 8;
11251134
}
1126-
if (len)
1127-
KeccakF1600(A);
11281135
}
11291136
}
11301137
#endif

crypto/sha/sha3.c

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
#include <string.h>
1111
#include "internal/sha3.h"
1212

13-
void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r);
13+
void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next);
1414

1515
void ossl_sha3_reset(KECCAK1600_CTX *ctx)
1616
{
1717
memset(ctx->A, 0, sizeof(ctx->A));
1818
ctx->bufsz = 0;
19+
ctx->xof_state = XOF_STATE_INIT;
1920
}
2021

2122
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)
5152
if (len == 0)
5253
return 1;
5354

55+
if (ctx->xof_state == XOF_STATE_SQUEEZE
56+
|| ctx->xof_state == XOF_STATE_FINAL)
57+
return 0;
58+
5459
if ((num = ctx->bufsz) != 0) { /* process intermediate buffer? */
5560
rem = bsz - num;
5661

@@ -84,13 +89,21 @@ int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len)
8489
return 1;
8590
}
8691

87-
int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx)
92+
/*
93+
* ossl_sha3_final()is a single shot method
94+
* (Use ossl_sha3_squeeze for multiple calls).
95+
* outlen is the variable size output.
96+
*/
97+
int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen)
8898
{
8999
size_t bsz = ctx->block_size;
90100
size_t num = ctx->bufsz;
91101

92-
if (ctx->md_size == 0)
102+
if (outlen == 0)
93103
return 1;
104+
if (ctx->xof_state == XOF_STATE_SQUEEZE
105+
|| ctx->xof_state == XOF_STATE_FINAL)
106+
return 0;
94107

95108
/*
96109
* 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)
103116

104117
(void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
105118

106-
SHA3_squeeze(ctx->A, md, ctx->md_size, bsz);
119+
ctx->xof_state = XOF_STATE_FINAL;
120+
SHA3_squeeze(ctx->A, out, outlen, bsz, 0);
121+
return 1;
122+
}
123+
124+
/*
125+
* This method can be called multiple times.
126+
* Rather than heavily modifying assembler for SHA3_squeeze(),
127+
* we instead just use the limitations of the existing function.
128+
* i.e. Only request multiples of the ctx->block_size when calling
129+
* SHA3_squeeze(). For output length requests smaller than the
130+
* ctx->block_size just request a single ctx->block_size bytes and
131+
* buffer the results. The next request will use the buffer first
132+
* to grab output bytes.
133+
*/
134+
int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen)
135+
{
136+
size_t bsz = ctx->block_size;
137+
size_t num = ctx->bufsz;
138+
size_t len;
139+
int next = 1;
140+
141+
if (outlen == 0)
142+
return 1;
143+
144+
if (ctx->xof_state == XOF_STATE_FINAL)
145+
return 0;
146+
147+
/*
148+
* On the first squeeze call, finish the absorb process,
149+
* by adding the trailing padding and then doing
150+
* a final absorb.
151+
*/
152+
if (ctx->xof_state != XOF_STATE_SQUEEZE) {
153+
/*
154+
* Pad the data with 10*1. Note that |num| can be |bsz - 1|
155+
* in which case both byte operations below are performed on
156+
* same byte...
157+
*/
158+
memset(ctx->buf + num, 0, bsz - num);
159+
ctx->buf[num] = ctx->pad;
160+
ctx->buf[bsz - 1] |= 0x80;
161+
(void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
162+
ctx->xof_state = XOF_STATE_SQUEEZE;
163+
num = ctx->bufsz = 0;
164+
next = 0;
165+
}
166+
167+
/*
168+
* Step 1. Consume any bytes left over from a previous squeeze
169+
* (See Step 4 below).
170+
*/
171+
if (num != 0) {
172+
if (outlen > ctx->bufsz)
173+
len = ctx->bufsz;
174+
else
175+
len = outlen;
176+
memcpy(out, ctx->buf + bsz - ctx->bufsz, len);
177+
out += len;
178+
outlen -= len;
179+
ctx->bufsz -= len;
180+
}
181+
if (outlen == 0)
182+
return 1;
183+
184+
/* Step 2. Copy full sized squeezed blocks to the output buffer directly */
185+
if (outlen >= bsz) {
186+
len = bsz * (outlen / bsz);
187+
SHA3_squeeze(ctx->A, out, len, bsz, next);
188+
next = 1;
189+
out += len;
190+
outlen -= len;
191+
}
192+
if (outlen > 0) {
193+
/* Step 3. Squeeze one more block into a buffer */
194+
SHA3_squeeze(ctx->A, ctx->buf, bsz, bsz, next);
195+
memcpy(out, ctx->buf, outlen);
196+
/* Step 4. Remember the leftover part of the squeezed block */
197+
ctx->bufsz = bsz - outlen;
198+
}
107199

108200
return 1;
109201
}

0 commit comments

Comments
 (0)