diff --git a/cipher/hash-common.c b/cipher/hash-common.c
index a750d644..74675d49 100644
--- a/cipher/hash-common.c
+++ b/cipher/hash-common.c
@@ -1,167 +1,185 @@
/* hash-common.c - Common code for hash algorithms
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
#include
#include
#include
#include
#ifdef HAVE_STDINT_H
# include
#endif
#include "g10lib.h"
+#include "bufhelp.h"
#include "hash-common.h"
/* Run a selftest for hash algorithm ALGO. If the resulting digest
matches EXPECT/EXPECTLEN and everything else is fine as well,
return NULL. If an error occurs, return a static text string
describing the error.
DATAMODE controls what will be hashed according to this table:
0 - Hash the supplied DATA of DATALEN.
1 - Hash one million times a 'a'. DATA and DATALEN are ignored.
*/
const char *
_gcry_hash_selftest_check_one (int algo,
int datamode, const void *data, size_t datalen,
const void *expect, size_t expectlen)
{
const char *result = NULL;
gcry_error_t err = 0;
gcry_md_hd_t hd;
unsigned char *digest;
char aaa[1000];
int xof = 0;
if (_gcry_md_get_algo_dlen (algo) == 0)
xof = 1;
else if (_gcry_md_get_algo_dlen (algo) != expectlen)
return "digest size does not match expected size";
err = _gcry_md_open (&hd, algo, 0);
if (err)
return "gcry_md_open failed";
switch (datamode)
{
case 0:
_gcry_md_write (hd, data, datalen);
break;
case 1: /* Hash one million times an "a". */
{
int i;
/* Write in odd size chunks so that we test the buffering. */
memset (aaa, 'a', 1000);
for (i = 0; i < 1000; i++)
_gcry_md_write (hd, aaa, 1000);
}
break;
default:
result = "invalid DATAMODE";
}
if (!result)
{
if (!xof)
{
digest = _gcry_md_read (hd, algo);
if ( memcmp (digest, expect, expectlen) )
result = "digest mismatch";
}
else
{
gcry_assert(expectlen <= sizeof(aaa));
err = _gcry_md_extract (hd, algo, aaa, expectlen);
if (err)
result = "error extracting output from XOF";
else if ( memcmp (aaa, expect, expectlen) )
result = "digest mismatch";
}
}
_gcry_md_close (hd);
return result;
}
/* Common function to write a chunk of data to the transform function
of a hash algorithm. Note that the use of the term "block" does
not imply a fixed size block. Note that we explicitly allow to use
this function after the context has been finalized; the result does
not have any meaning but writing after finalize is sometimes
helpful to mitigate timing attacks. */
void
_gcry_md_block_write (void *context, const void *inbuf_arg, size_t inlen)
{
const unsigned char *inbuf = inbuf_arg;
gcry_md_block_ctx_t *hd = context;
unsigned int stack_burn = 0;
+ unsigned int nburn;
const unsigned int blocksize = hd->blocksize;
size_t inblocks;
+ size_t copylen;
if (sizeof(hd->buf) < blocksize)
BUG();
if (!hd->bwrite)
return;
- if (hd->count == blocksize) /* Flush the buffer. */
+ while (hd->count)
{
- stack_burn = hd->bwrite (hd, hd->buf, 1);
- _gcry_burn_stack (stack_burn);
- stack_burn = 0;
- hd->count = 0;
- if (!++hd->nblocks)
- hd->nblocks_high++;
- }
- if (!inbuf)
- return;
+ if (hd->count == blocksize) /* Flush the buffer. */
+ {
+ nburn = hd->bwrite (hd, hd->buf, 1);
+ stack_burn = nburn > stack_burn ? nburn : stack_burn;
+ hd->count = 0;
+ if (!++hd->nblocks)
+ hd->nblocks_high++;
+ }
+ else
+ {
+ copylen = inlen;
+ if (copylen > blocksize - hd->count)
+ copylen = blocksize - hd->count;
- if (hd->count)
- {
- for (; inlen && hd->count < blocksize; inlen--)
- hd->buf[hd->count++] = *inbuf++;
- _gcry_md_block_write (hd, NULL, 0);
- if (!inlen)
- return;
+ if (copylen == 0)
+ break;
+
+ buf_cpy (&hd->buf[hd->count], inbuf, copylen);
+ hd->count += copylen;
+ inbuf += copylen;
+ inlen -= copylen;
+ }
}
+ if (inlen == 0)
+ return;
+
if (inlen >= blocksize)
{
inblocks = inlen / blocksize;
- stack_burn = hd->bwrite (hd, inbuf, inblocks);
+ nburn = hd->bwrite (hd, inbuf, inblocks);
+ stack_burn = nburn > stack_burn ? nburn : stack_burn;
hd->count = 0;
hd->nblocks_high += (hd->nblocks + inblocks < inblocks);
hd->nblocks += inblocks;
inlen -= inblocks * blocksize;
inbuf += inblocks * blocksize;
}
- _gcry_burn_stack (stack_burn);
- for (; inlen && hd->count < blocksize; inlen--)
- hd->buf[hd->count++] = *inbuf++;
+
+ if (inlen)
+ {
+ buf_cpy (hd->buf, inbuf, inlen);
+ hd->count = inlen;
+ }
+
+ if (stack_burn > 0)
+ _gcry_burn_stack (stack_burn);
}