Add SHA-3 support to secure-hash
* admin/merge-gnulib (GNULIB_MODULES): Add crypto/sha3-buffer. * lib/sha3.c: New file, imported by running admin/merge-gnulib. * lib/sha3.h: Likewise. * m4/sha3.m4: Likewise. * lib/gnulib.mk.in: Updated by admin/merge-gnulib. * m4/gnulib-comp.m4: Likewise. * src/fns.c: Include sha3.h (Fsecure_hash_algorithms): Add Qsha3_224, Qsha3_256, Qsha3_384, and Qsha3_512. (secure_hash): Likewise. (Fsecure_hash): List the SHA-3 algorithms in the docstring. (syms_of_fns): Define Qsha3_224, Qsha3_256, Qsha3_384, and Qsha3_512. * test/lisp/net/gnutls-tests.el (gnutls-tests-internal-macs-upcased): Filter out the new SHA-3 algorithms since they are currently not implemented in gnutls. * test/src/fns-tests.el (test-secure-hash): Add test cases for the new algorithms. * doc/lispref/text.texi (Checksum/Hash): List the SHA-3 algorithms. Mention that they are considered secure. * etc/NEWS: Mention the new feature.
This commit is contained in:
@@ -35,6 +35,7 @@ GNULIB_MODULES='
|
||||
careadlinkat close-stream copy-file-range
|
||||
crypto/md5 crypto/md5-buffer
|
||||
crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer
|
||||
crypto/sha3-buffer
|
||||
d-type diffseq double-slash-root dtoastr dtotimespec dup2
|
||||
environ execinfo faccessat
|
||||
fchmodat fcntl fcntl-h fdopendir file-has-acl
|
||||
|
||||
@@ -4983,15 +4983,15 @@ that you have an unaltered copy of that data.
|
||||
|
||||
@cindex message digest
|
||||
Emacs supports several common cryptographic hash algorithms: MD5,
|
||||
SHA-1, SHA-2, SHA-224, SHA-256, SHA-384 and SHA-512. MD5 is the
|
||||
oldest of these algorithms, and is commonly used in @dfn{message
|
||||
digests} to check the integrity of messages transmitted over a
|
||||
network. MD5 and SHA-1 are not collision resistant (i.e., it is
|
||||
possible to deliberately design different pieces of data which have
|
||||
the same MD5 or SHA-1 hash), so you should not use them for anything
|
||||
security-related. For security-related applications you should use
|
||||
the other hash types, such as SHA-2 (e.g., @code{sha256} or
|
||||
@code{sha512}).
|
||||
SHA-1, SHA-2, SHA-224, SHA-256, SHA-384, SHA-512, SHA3-224, SHA3-256,
|
||||
SHA3-384, and SHA3-512. MD5 is the oldest of these algorithms, and is
|
||||
commonly used in @dfn{message digests} to check the integrity of
|
||||
messages transmitted over a network. MD5 and SHA-1 are not collision
|
||||
resistant (i.e., it is possible to deliberately design different pieces
|
||||
of data which have the same MD5 or SHA-1 hash), so you should not use
|
||||
them for anything security-related. For security-related applications
|
||||
you should use the other hash types, such as SHA-2 (e.g., @code{sha256}
|
||||
or @code{sha512}) or SHA-3 (e.g., @code{sha3-256} or @code{sha3-512}).
|
||||
|
||||
@defun secure-hash-algorithms
|
||||
This function returns a list of symbols representing algorithms that
|
||||
@@ -5001,8 +5001,9 @@ This function returns a list of symbols representing algorithms that
|
||||
@defun secure-hash algorithm object &optional start end binary
|
||||
This function returns a hash for @var{object}. The argument
|
||||
@var{algorithm} is a symbol stating which hash to compute: one of
|
||||
@code{md5}, @code{sha1}, @code{sha224}, @code{sha256}, @code{sha384}
|
||||
or @code{sha512}. The argument @var{object} should be a buffer or a
|
||||
@code{md5}, @code{sha1}, @code{sha224}, @code{sha256}, @code{sha384},
|
||||
@code{sha512}, @code{sha3-224}, @code{sha3-256}, @code{sha3-384}, or
|
||||
@code{sha3-512}. The argument @var{object} should be a buffer or a
|
||||
string.
|
||||
|
||||
The optional arguments @var{start} and @var{end} are character
|
||||
@@ -5035,6 +5036,18 @@ non-@code{nil}).
|
||||
@item
|
||||
For @code{sha512}: 128 characters (64 bytes if @var{binary} is
|
||||
non-@code{nil}).
|
||||
@item
|
||||
For @code{sha3-224}: 56 characters (28 bytes if @var{binary} is
|
||||
non-@code{nil}).
|
||||
@item
|
||||
For @code{sha3-256}: 64 characters (32 bytes if @var{binary} is
|
||||
non-@code{nil}).
|
||||
@item
|
||||
For @code{sha3-384}: 96 characters (48 bytes if @var{binary} is
|
||||
non-@code{nil}).
|
||||
@item
|
||||
For @code{sha3-512}: 128 characters (64 bytes if @var{binary} is
|
||||
non-@code{nil}).
|
||||
@end itemize
|
||||
|
||||
This function does not compute the hash directly from the internal
|
||||
|
||||
7
etc/NEWS
7
etc/NEWS
@@ -3955,6 +3955,13 @@ that will provide an Xref backend when used.
|
||||
|
||||
* Lisp Changes in Emacs 31.1
|
||||
|
||||
+++
|
||||
** 'secure-hash' now supports generating SHA-3 message digests.
|
||||
The list returned by 'secure-hash-algorithms' now contains the symbols
|
||||
'sha3-224', 'sha3-256', 'sha3-384', and 'sha3-512'. These symbols can
|
||||
be used as the ALGORITHM argument of 'secure-hash' to generate SHA-3
|
||||
hashes.
|
||||
|
||||
+++
|
||||
** New function 'garbage-collect-heapsize'.
|
||||
Same as 'garbage-collect' but just returns the info from the last GC
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
# crypto/md5-buffer \
|
||||
# crypto/sha1-buffer \
|
||||
# crypto/sha256-buffer \
|
||||
# crypto/sha3-buffer \
|
||||
# crypto/sha512-buffer \
|
||||
# d-type \
|
||||
# diffseq \
|
||||
@@ -1853,6 +1854,16 @@ EXTRA_DIST += gl_openssl.h sha256.h
|
||||
endif
|
||||
## end gnulib module crypto/sha256-buffer
|
||||
|
||||
## begin gnulib module crypto/sha3-buffer
|
||||
ifeq (,$(OMIT_GNULIB_MODULE_crypto/sha3-buffer))
|
||||
|
||||
libgnu_a_SOURCES += sha3.c
|
||||
|
||||
EXTRA_DIST += sha3.h
|
||||
|
||||
endif
|
||||
## end gnulib module crypto/sha3-buffer
|
||||
|
||||
## begin gnulib module crypto/sha512-buffer
|
||||
ifeq (,$(OMIT_GNULIB_MODULE_crypto/sha512-buffer))
|
||||
|
||||
|
||||
442
lib/sha3.c
Normal file
442
lib/sha3.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/* sha3.c - Functions to calculate SHA-3 hashes as specified by FIPS-202.
|
||||
Copyright (C) 2025-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file 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.
|
||||
|
||||
This file 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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Collin Funk <collin.funk1@gmail.com>, 2025. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "sha3.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <byteswap.h>
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
# define SWAP(n) u64bswap (n)
|
||||
#else
|
||||
# define SWAP(n) (n)
|
||||
#endif
|
||||
|
||||
#if ! HAVE_OPENSSL_SHA3
|
||||
|
||||
static const u64 rc[] = {
|
||||
u64init (0x00000000, 0x00000001), u64init (0x00000000, 0x00008082),
|
||||
u64init (0x80000000, 0x0000808A), u64init (0x80000000, 0x80008000),
|
||||
u64init (0x00000000, 0x0000808B), u64init (0x00000000, 0x80000001),
|
||||
u64init (0x80000000, 0x80008081), u64init (0x80000000, 0x00008009),
|
||||
u64init (0x00000000, 0x0000008A), u64init (0x00000000, 0x00000088),
|
||||
u64init (0x00000000, 0x80008009), u64init (0x00000000, 0x8000000A),
|
||||
u64init (0x00000000, 0x8000808B), u64init (0x80000000, 0x0000008B),
|
||||
u64init (0x80000000, 0x00008089), u64init (0x80000000, 0x00008003),
|
||||
u64init (0x80000000, 0x00008002), u64init (0x80000000, 0x00000080),
|
||||
u64init (0x00000000, 0x0000800A), u64init (0x80000000, 0x8000000A),
|
||||
u64init (0x80000000, 0x80008081), u64init (0x80000000, 0x00008080),
|
||||
u64init (0x00000000, 0x80000001), u64init (0x80000000, 0x80008008)
|
||||
};
|
||||
|
||||
#define DEFINE_SHA3_INIT_CTX(SIZE) \
|
||||
bool \
|
||||
sha3_##SIZE##_init_ctx (struct sha3_ctx *ctx) \
|
||||
{ \
|
||||
memset (&ctx->state, '\0', sizeof ctx->state); \
|
||||
ctx->buflen = 0; \
|
||||
ctx->digestlen = SHA3_##SIZE##_DIGEST_SIZE; \
|
||||
ctx->blocklen = SHA3_##SIZE##_BLOCK_SIZE; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
DEFINE_SHA3_INIT_CTX (224)
|
||||
DEFINE_SHA3_INIT_CTX (256)
|
||||
DEFINE_SHA3_INIT_CTX (384)
|
||||
DEFINE_SHA3_INIT_CTX (512)
|
||||
|
||||
void
|
||||
sha3_free_ctx (_GL_UNUSED struct sha3_ctx *ctx)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
/* Copy the value from V into the memory location pointed to by *CP,
|
||||
If your architecture allows unaligned access, this is equivalent to
|
||||
* (__typeof__ (v) *) cp = v */
|
||||
static void
|
||||
set_uint64 (char *cp, u64 v)
|
||||
{
|
||||
memcpy (cp, &v, sizeof v);
|
||||
}
|
||||
|
||||
void *
|
||||
sha3_read_ctx (struct sha3_ctx const *restrict ctx, void *restrict resbuf)
|
||||
{
|
||||
char *r = resbuf;
|
||||
size_t words = ctx->digestlen / sizeof *ctx->state;
|
||||
size_t bytes = ctx->digestlen % sizeof *ctx->state;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < words; ++i, r += sizeof *ctx->state)
|
||||
set_uint64 (r, SWAP (ctx->state[i]));
|
||||
if (bytes)
|
||||
{
|
||||
u64 word = ctx->state[i];
|
||||
do
|
||||
{
|
||||
*r++ = u64getlo (word) & 0xFF;
|
||||
word = u64shr (word, 8);
|
||||
}
|
||||
while (--bytes);
|
||||
}
|
||||
return resbuf;
|
||||
}
|
||||
|
||||
static void
|
||||
sha3_conclude_ctx (struct sha3_ctx *ctx)
|
||||
{
|
||||
ctx->buffer[ctx->buflen++] = 0x06;
|
||||
memset (ctx->buffer + ctx->buflen, '\0', ctx->blocklen - ctx->buflen);
|
||||
ctx->buffer[ctx->blocklen - 1] |= 0x80;
|
||||
sha3_process_block (ctx->buffer, ctx->blocklen, ctx);
|
||||
}
|
||||
|
||||
void *
|
||||
sha3_finish_ctx (struct sha3_ctx *restrict ctx, void *restrict resbuf)
|
||||
{
|
||||
sha3_conclude_ctx (ctx);
|
||||
return sha3_read_ctx (ctx, resbuf);
|
||||
}
|
||||
|
||||
#define DEFINE_SHA3_BUFFER(SIZE) \
|
||||
void * \
|
||||
sha3_##SIZE##_buffer (char const *restrict buffer, size_t len, \
|
||||
void *restrict resblock) \
|
||||
{ \
|
||||
struct sha3_ctx ctx; \
|
||||
sha3_##SIZE##_init_ctx (&ctx); \
|
||||
sha3_process_bytes (buffer, len, &ctx); \
|
||||
return sha3_finish_ctx (&ctx, resblock); \
|
||||
}
|
||||
|
||||
DEFINE_SHA3_BUFFER (224)
|
||||
DEFINE_SHA3_BUFFER (256)
|
||||
DEFINE_SHA3_BUFFER (384)
|
||||
DEFINE_SHA3_BUFFER (512)
|
||||
|
||||
bool
|
||||
sha3_process_bytes (void const *restrict buffer, size_t len,
|
||||
struct sha3_ctx *restrict ctx)
|
||||
{
|
||||
char const *buf = buffer;
|
||||
|
||||
if (0 < ctx->buflen)
|
||||
{
|
||||
size_t left = ctx->blocklen - ctx->buflen;
|
||||
if (len < left)
|
||||
{
|
||||
/* Not enough to fill a full block. */
|
||||
memcpy (ctx->buffer + ctx->buflen, buf, len);
|
||||
ctx->buflen += len;
|
||||
return true;
|
||||
}
|
||||
/* Process the block that already had bytes buffered. */
|
||||
memcpy (ctx->buffer + ctx->buflen, buf, left);
|
||||
buf += left;
|
||||
len -= left;
|
||||
sha3_process_block (ctx->buffer, ctx->blocklen, ctx);
|
||||
}
|
||||
|
||||
/* Process as many complete blocks as possible. */
|
||||
size_t full_len = len - len % ctx->blocklen;
|
||||
sha3_process_block (buf, full_len, ctx);
|
||||
buf += full_len;
|
||||
len -= full_len;
|
||||
|
||||
memcpy (ctx->buffer, buf, len);
|
||||
ctx->buflen = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sha3_process_block (void const *restrict buffer, size_t len,
|
||||
struct sha3_ctx *restrict ctx)
|
||||
{
|
||||
u64 *a = ctx->state;
|
||||
const u64 *words = buffer;
|
||||
size_t nwords = len / sizeof *words;
|
||||
const u64 *endp = words + nwords;
|
||||
|
||||
while (words < endp)
|
||||
{
|
||||
for (size_t i = 0; i < ctx->blocklen / sizeof *ctx->state; ++i, ++words)
|
||||
ctx->state[i] = u64xor (ctx->state[i], SWAP (*words));
|
||||
for (int i = 0; i < 24; ++i)
|
||||
{
|
||||
u64 c[5];
|
||||
u64 d[5];
|
||||
u64 t1;
|
||||
u64 t2;
|
||||
|
||||
/* Theta step 1. */
|
||||
c[0] = u64xor (u64xor (u64xor (u64xor (a[0], a[5]), a[10]),
|
||||
a[15]), a[20]);
|
||||
c[1] = u64xor (u64xor (u64xor (u64xor (a[1], a[6]), a[11]),
|
||||
a[16]), a[21]);
|
||||
c[2] = u64xor (u64xor (u64xor (u64xor (a[2], a[7]), a[12]),
|
||||
a[17]), a[22]);
|
||||
c[3] = u64xor (u64xor (u64xor (u64xor (a[3], a[8]), a[13]),
|
||||
a[18]), a[23]);
|
||||
c[4] = u64xor (u64xor (u64xor (u64xor (a[4], a[9]), a[14]),
|
||||
a[19]), a[24]);
|
||||
|
||||
/* Theta step 2. */
|
||||
d[0] = u64xor (c[4], u64rol (c[1], 1));
|
||||
d[1] = u64xor (c[0], u64rol (c[2], 1));
|
||||
d[2] = u64xor (c[1], u64rol (c[3], 1));
|
||||
d[3] = u64xor (c[2], u64rol (c[4], 1));
|
||||
d[4] = u64xor (c[3], u64rol (c[0], 1));
|
||||
|
||||
/* Theta step 3. */
|
||||
a[0] = u64xor (a[0], d[0]);
|
||||
a[5] = u64xor (a[5], d[0]);
|
||||
a[10] = u64xor (a[10], d[0]);
|
||||
a[15] = u64xor (a[15], d[0]);
|
||||
a[20] = u64xor (a[20], d[0]);
|
||||
a[1] = u64xor (a[1], d[1]);
|
||||
a[6] = u64xor (a[6], d[1]);
|
||||
a[11] = u64xor (a[11], d[1]);
|
||||
a[16] = u64xor (a[16], d[1]);
|
||||
a[21] = u64xor (a[21], d[1]);
|
||||
a[2] = u64xor (a[2], d[2]);
|
||||
a[7] = u64xor (a[7], d[2]);
|
||||
a[12] = u64xor (a[12], d[2]);
|
||||
a[17] = u64xor (a[17], d[2]);
|
||||
a[22] = u64xor (a[22], d[2]);
|
||||
a[3] = u64xor (a[3], d[3]);
|
||||
a[8] = u64xor (a[8], d[3]);
|
||||
a[13] = u64xor (a[13], d[3]);
|
||||
a[18] = u64xor (a[18], d[3]);
|
||||
a[23] = u64xor (a[23], d[3]);
|
||||
a[4] = u64xor (a[4], d[4]);
|
||||
a[9] = u64xor (a[9], d[4]);
|
||||
a[14] = u64xor (a[14], d[4]);
|
||||
a[19] = u64xor (a[19], d[4]);
|
||||
a[24] = u64xor (a[24], d[4]);
|
||||
|
||||
/* Rho and Pi. */
|
||||
t1 = a[1];
|
||||
t2 = u64rol (t1, 1);
|
||||
t1 = a[10];
|
||||
a[10] = t2;
|
||||
t2 = u64rol (t1, 3);
|
||||
t1 = a[7];
|
||||
a[7] = t2;
|
||||
t2 = u64rol (t1, 6);
|
||||
t1 = a[11];
|
||||
a[11] = t2;
|
||||
t2 = u64rol (t1, 10);
|
||||
t1 = a[17];
|
||||
a[17] = t2;
|
||||
t2 = u64rol (t1, 15);
|
||||
t1 = a[18];
|
||||
a[18] = t2;
|
||||
t2 = u64rol (t1, 21);
|
||||
t1 = a[3];
|
||||
a[3] = t2;
|
||||
t2 = u64rol (t1, 28);
|
||||
t1 = a[5];
|
||||
a[5] = t2;
|
||||
t2 = u64rol (t1, 36);
|
||||
t1 = a[16];
|
||||
a[16] = t2;
|
||||
t2 = u64rol (t1, 45);
|
||||
t1 = a[8];
|
||||
a[8] = t2;
|
||||
t2 = u64rol (t1, 55);
|
||||
t1 = a[21];
|
||||
a[21] = t2;
|
||||
t2 = u64rol (t1, 2);
|
||||
t1 = a[24];
|
||||
a[24] = t2;
|
||||
t2 = u64rol (t1, 14);
|
||||
t1 = a[4];
|
||||
a[4] = t2;
|
||||
t2 = u64rol (t1, 27);
|
||||
t1 = a[15];
|
||||
a[15] = t2;
|
||||
t2 = u64rol (t1, 41);
|
||||
t1 = a[23];
|
||||
a[23] = t2;
|
||||
t2 = u64rol (t1, 56);
|
||||
t1 = a[19];
|
||||
a[19] = t2;
|
||||
t2 = u64rol (t1, 8);
|
||||
t1 = a[13];
|
||||
a[13] = t2;
|
||||
t2 = u64rol (t1, 25);
|
||||
t1 = a[12];
|
||||
a[12] = t2;
|
||||
t2 = u64rol (t1, 43);
|
||||
t1 = a[2];
|
||||
a[2] = t2;
|
||||
t2 = u64rol (t1, 62);
|
||||
t1 = a[20];
|
||||
a[20] = t2;
|
||||
t2 = u64rol (t1, 18);
|
||||
t1 = a[14];
|
||||
a[14] = t2;
|
||||
t2 = u64rol (t1, 39);
|
||||
t1 = a[22];
|
||||
a[22] = t2;
|
||||
t2 = u64rol (t1, 61);
|
||||
t1 = a[9];
|
||||
a[9] = t2;
|
||||
t2 = u64rol (t1, 20);
|
||||
t1 = a[6];
|
||||
a[6] = t2;
|
||||
t2 = u64rol (t1, 44);
|
||||
t1 = a[1];
|
||||
a[1] = t2;
|
||||
|
||||
/* Chi. */
|
||||
for (int j = 0; j < 25; j += 5)
|
||||
{
|
||||
t1 = a[j];
|
||||
t2 = a[j + 1];
|
||||
a[j] = u64xor (a[j], u64and (u64not (a[j + 1]), a[j + 2]));
|
||||
a[j + 1] = u64xor (a[j + 1], u64and (u64not (a[j + 2]),
|
||||
a[j + 3]));
|
||||
a[j + 2] = u64xor (a[j + 2], u64and (u64not (a[j + 3]),
|
||||
a[j + 4]));
|
||||
a[j + 3] = u64xor (a[j + 3], u64and (u64not (a[j + 4]), t1));
|
||||
a[j + 4] = u64xor (a[j + 4], u64and (u64not (t1), t2));
|
||||
}
|
||||
|
||||
/* Iota. */
|
||||
a[0] = u64xor (a[0], rc[i]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else /* OpenSSL implementation. */
|
||||
|
||||
/* We avoid using all of EVP error strings. Just guess a reasonable errno. */
|
||||
#include <errno.h>
|
||||
|
||||
#define DEFINE_SHA3_INIT_CTX(SIZE) \
|
||||
bool \
|
||||
sha3_##SIZE##_init_ctx (struct sha3_ctx *ctx) \
|
||||
{ \
|
||||
EVP_MD_CTX *evp_ctx = EVP_MD_CTX_new (); \
|
||||
if (evp_ctx && ! EVP_DigestInit_ex (evp_ctx, EVP_sha3_##SIZE (), NULL)) \
|
||||
{ \
|
||||
EVP_MD_CTX_free (evp_ctx); \
|
||||
evp_ctx = NULL; \
|
||||
} \
|
||||
ctx->evp_ctx = evp_ctx; \
|
||||
errno = ENOMEM; /* OK to set errno even if successful. */ \
|
||||
return !!evp_ctx; \
|
||||
}
|
||||
|
||||
DEFINE_SHA3_INIT_CTX (224)
|
||||
DEFINE_SHA3_INIT_CTX (256)
|
||||
DEFINE_SHA3_INIT_CTX (384)
|
||||
DEFINE_SHA3_INIT_CTX (512)
|
||||
|
||||
void
|
||||
sha3_free_ctx (struct sha3_ctx *ctx)
|
||||
{
|
||||
if (ctx->evp_ctx != NULL)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
EVP_MD_CTX_free (ctx->evp_ctx);
|
||||
ctx->evp_ctx = NULL;
|
||||
errno = saved_errno;
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
sha3_read_ctx (struct sha3_ctx const *restrict ctx, void *restrict resbuf)
|
||||
{
|
||||
void *result = NULL;
|
||||
int err = ENOMEM;
|
||||
EVP_MD_CTX *evp_ctx = EVP_MD_CTX_new ();
|
||||
if (evp_ctx)
|
||||
{
|
||||
if (EVP_MD_CTX_copy_ex (evp_ctx, ctx->evp_ctx))
|
||||
{
|
||||
if (EVP_DigestFinal_ex (evp_ctx, resbuf, 0))
|
||||
result = resbuf;
|
||||
err = EINVAL;
|
||||
}
|
||||
EVP_MD_CTX_free (evp_ctx);
|
||||
}
|
||||
errno = err; /* OK to set errno even if successful. */
|
||||
return result;
|
||||
}
|
||||
|
||||
void *
|
||||
sha3_finish_ctx (struct sha3_ctx *restrict ctx, void *restrict resbuf)
|
||||
{
|
||||
int result = EVP_DigestFinal_ex (ctx->evp_ctx, resbuf, NULL);
|
||||
if (result == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
return resbuf;
|
||||
}
|
||||
|
||||
#define DEFINE_SHA3_BUFFER(SIZE) \
|
||||
void * \
|
||||
sha3_##SIZE##_buffer (char const *restrict buffer, size_t len, \
|
||||
void *restrict resblock) \
|
||||
{ \
|
||||
struct sha3_ctx ctx; \
|
||||
void *result = ((sha3_##SIZE##_init_ctx (&ctx) \
|
||||
&& sha3_process_bytes (buffer, len, &ctx)) \
|
||||
? sha3_finish_ctx (&ctx, resblock) \
|
||||
: NULL); \
|
||||
sha3_free_ctx (&ctx); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_SHA3_BUFFER (224)
|
||||
DEFINE_SHA3_BUFFER (256)
|
||||
DEFINE_SHA3_BUFFER (384)
|
||||
DEFINE_SHA3_BUFFER (512)
|
||||
|
||||
bool
|
||||
sha3_process_bytes (void const *restrict buffer, size_t len,
|
||||
struct sha3_ctx *restrict ctx)
|
||||
{
|
||||
int result = EVP_DigestUpdate (ctx->evp_ctx, buffer, len);
|
||||
if (result == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sha3_process_block (void const *restrict buffer, size_t len,
|
||||
struct sha3_ctx *restrict ctx)
|
||||
{
|
||||
return sha3_process_bytes (buffer, len, ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
132
lib/sha3.h
Normal file
132
lib/sha3.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* sha3.h - Functions to calculate SHA-3 hashes as specified by FIPS-202.
|
||||
Copyright (C) 2025-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file 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.
|
||||
|
||||
This file 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 <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Collin Funk <collin.funk1@gmail.com>, 2025. */
|
||||
|
||||
#ifndef SHA3_H
|
||||
# define SHA3_H 1
|
||||
|
||||
# include <stddef.h>
|
||||
# include <stdio.h>
|
||||
# include <stdint.h>
|
||||
|
||||
# include "u64.h"
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
/* OpenSSL does not have the Init, Update, Final API for SHA-3. We must use
|
||||
the EVP API. */
|
||||
# if HAVE_OPENSSL_SHA3
|
||||
# include <openssl/evp.h>
|
||||
# endif
|
||||
|
||||
/* Digest sizes in bytes. */
|
||||
enum { SHA3_224_DIGEST_SIZE = 224 / 8 };
|
||||
enum { SHA3_256_DIGEST_SIZE = 256 / 8 };
|
||||
enum { SHA3_384_DIGEST_SIZE = 384 / 8 };
|
||||
enum { SHA3_512_DIGEST_SIZE = 512 / 8 };
|
||||
|
||||
/* Block sizes in bytes. */
|
||||
enum { SHA3_224_BLOCK_SIZE = 1152 / 8 };
|
||||
enum { SHA3_256_BLOCK_SIZE = 1088 / 8 };
|
||||
enum { SHA3_384_BLOCK_SIZE = 832 / 8 };
|
||||
enum { SHA3_512_BLOCK_SIZE = 576 / 8 };
|
||||
|
||||
/* Structure to save state of computation between the single steps. */
|
||||
struct sha3_ctx
|
||||
{
|
||||
# if HAVE_OPENSSL_SHA3
|
||||
/* EVP_MD_CTX is an incomplete type. It cannot be placed on the stack. */
|
||||
EVP_MD_CTX *evp_ctx;
|
||||
# else
|
||||
u64 state[25];
|
||||
uint8_t buffer[144]; /* Up to BLOCKLEN in use. */
|
||||
size_t buflen; /* ≥ 0, ≤ BLOCKLEN */
|
||||
size_t digestlen; /* One of SHA3_{224,256,384,512}_DIGEST_SIZE. */
|
||||
size_t blocklen; /* One of SHA3_{224,256,384,512}_BLOCK_SIZE. */
|
||||
# endif
|
||||
};
|
||||
|
||||
/* Initialize structure containing state of computation. */
|
||||
extern bool sha3_224_init_ctx (struct sha3_ctx *ctx);
|
||||
extern bool sha3_256_init_ctx (struct sha3_ctx *ctx);
|
||||
extern bool sha3_384_init_ctx (struct sha3_ctx *ctx);
|
||||
extern bool sha3_512_init_ctx (struct sha3_ctx *ctx);
|
||||
|
||||
/* Free memory allocated by the init_structure. */
|
||||
extern void sha3_free_ctx (struct sha3_ctx *ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is necessary that LEN is a multiple of the BLOCKLEN member of CTX!!!
|
||||
Return false if an OpenSSL function fails. */
|
||||
extern bool sha3_process_block (void const *restrict buffer, size_t len,
|
||||
struct sha3_ctx *restrict ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
initialization function update the context for the next LEN bytes
|
||||
starting at BUFFER.
|
||||
It is NOT required that LEN is a multiple of the BLOCKLEN member of CTX.
|
||||
Return false if an OpenSSL function fails. */
|
||||
extern bool sha3_process_bytes (void const *restrict buffer, size_t len,
|
||||
struct sha3_ctx *restrict ctx);
|
||||
|
||||
/* Process the remaining bytes in the buffer and put result from CTX in RESBUF.
|
||||
The result is always in little endian byte order, so that a byte-wise output
|
||||
yields to the wanted ASCII representation of the message digest.
|
||||
Return NULL if an OpenSSL function fails. */
|
||||
extern void *sha3_finish_ctx (struct sha3_ctx *restrict ctx,
|
||||
void *restrict resbuf);
|
||||
|
||||
/* Put result from CTX in RESBUF. The result is always in little endian byte
|
||||
order, so that a byte-wise output yields to the wanted ASCII representation
|
||||
of the message digest.
|
||||
Return NULL if an OpenSSL function fails. */
|
||||
extern void *sha3_read_ctx (struct sha3_ctx const *restrict ctx,
|
||||
void *restrict resbuf);
|
||||
|
||||
/* Compute a SHA-3 message digest for LEN bytes beginning at BUFFER.
|
||||
The result is always in little endian byte order, so that a byte-wise
|
||||
output yields to the wanted ASCII representation of the message
|
||||
digest.
|
||||
Return NULL if an OpenSSL function fails. */
|
||||
extern void *sha3_224_buffer (char const *restrict buffer, size_t len,
|
||||
void *restrict resblock);
|
||||
extern void *sha3_256_buffer (char const *restrict buffer, size_t len,
|
||||
void *restrict resblock);
|
||||
extern void *sha3_384_buffer (char const *restrict buffer, size_t len,
|
||||
void *restrict resblock);
|
||||
extern void *sha3_512_buffer (char const *restrict buffer, size_t len,
|
||||
void *restrict resblock);
|
||||
|
||||
/* Compute SHA-3 message digest for bytes read from STREAM. STREAM is an open
|
||||
file stream. Regular files are handled more efficiently. The contents of
|
||||
STREAM from its current position to its end will be read. The case that the
|
||||
last operation on STREAM was an 'ungetc' is not supported. The resulting
|
||||
message digest number will be written into RESBLOCK. */
|
||||
extern int sha3_224_stream (FILE *restrict stream, void *restrict resblock);
|
||||
extern int sha3_256_stream (FILE *restrict stream, void *restrict resblock);
|
||||
extern int sha3_384_stream (FILE *restrict stream, void *restrict resblock);
|
||||
extern int sha3_512_stream (FILE *restrict stream, void *restrict resblock);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif
|
||||
@@ -70,6 +70,7 @@ AC_DEFUN([gl_EARLY],
|
||||
# Code from module crypto/md5-buffer:
|
||||
# Code from module crypto/sha1-buffer:
|
||||
# Code from module crypto/sha256-buffer:
|
||||
# Code from module crypto/sha3-buffer:
|
||||
# Code from module crypto/sha512-buffer:
|
||||
# Code from module d-type:
|
||||
# Code from module diffseq:
|
||||
@@ -286,6 +287,8 @@ AC_DEFUN([gl_INIT],
|
||||
AC_REQUIRE([AC_C_RESTRICT])
|
||||
gl_SHA256
|
||||
AC_REQUIRE([AC_C_RESTRICT])
|
||||
gl_SHA3
|
||||
AC_REQUIRE([AC_C_RESTRICT])
|
||||
gl_SHA512
|
||||
gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE
|
||||
gl_DIRENT_H
|
||||
@@ -1445,6 +1448,8 @@ AC_DEFUN([gl_FILE_LIST], [
|
||||
lib/sha1.h
|
||||
lib/sha256.c
|
||||
lib/sha256.h
|
||||
lib/sha3.c
|
||||
lib/sha3.h
|
||||
lib/sha512.c
|
||||
lib/sha512.h
|
||||
lib/sig2str.c
|
||||
@@ -1609,6 +1614,7 @@ AC_DEFUN([gl_FILE_LIST], [
|
||||
m4/selinux-selinux-h.m4
|
||||
m4/sha1.m4
|
||||
m4/sha256.m4
|
||||
m4/sha3.m4
|
||||
m4/sha512.m4
|
||||
m4/sig2str.m4
|
||||
m4/sigdescr_np.m4
|
||||
|
||||
16
m4/sha3.m4
Normal file
16
m4/sha3.m4
Normal file
@@ -0,0 +1,16 @@
|
||||
# sha3.m4
|
||||
# serial 1
|
||||
dnl Copyright (C) 2025-2026 Free Software Foundation, Inc.
|
||||
dnl This file is free software; the Free Software Foundation
|
||||
dnl gives unlimited permission to copy and/or distribute it,
|
||||
dnl with or without modifications, as long as this notice is preserved.
|
||||
dnl This file is offered as-is, without any warranty.
|
||||
|
||||
AC_DEFUN([gl_SHA3],
|
||||
[
|
||||
dnl Prerequisites of lib/sha3.c.
|
||||
AC_REQUIRE([gl_BIGENDIAN])
|
||||
|
||||
dnl Determine HAVE_OPENSSL_SHA3 and LIB_CRYPTO
|
||||
gl_CRYPTO_CHECK([SHA3])
|
||||
])
|
||||
58
src/fns.c
58
src/fns.c
@@ -6014,13 +6014,14 @@ DEFUN ("internal--hash-table-index-size",
|
||||
|
||||
|
||||
/************************************************************************
|
||||
MD5, SHA-1, and SHA-2
|
||||
MD5, SHA-1, SHA-2, and SHA-3
|
||||
************************************************************************/
|
||||
|
||||
#include "md5.h"
|
||||
#include "sha1.h"
|
||||
#include "sha256.h"
|
||||
#include "sha512.h"
|
||||
#include "sha3.h"
|
||||
|
||||
/* Store into HEXBUF an unterminated hexadecimal character string
|
||||
representing DIGEST, which is binary data of size DIGEST_SIZE bytes.
|
||||
@@ -6051,7 +6052,8 @@ DEFUN ("secure-hash-algorithms", Fsecure_hash_algorithms,
|
||||
doc: /* Return a list of all the supported `secure-hash' algorithms. */)
|
||||
(void)
|
||||
{
|
||||
return list (Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512);
|
||||
return list (Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512,
|
||||
Qsha3_224, Qsha3_256, Qsha3_384, Qsha3_512);
|
||||
}
|
||||
|
||||
/* Extract data from a string or a buffer. SPEC is a list of
|
||||
@@ -6290,6 +6292,26 @@ secure_hash (Lisp_Object algorithm, Lisp_Object object, Lisp_Object start,
|
||||
digest_size = SHA512_DIGEST_SIZE;
|
||||
hash_func = sha512_buffer;
|
||||
}
|
||||
else if (EQ (algorithm, Qsha3_224))
|
||||
{
|
||||
digest_size = SHA3_224_DIGEST_SIZE;
|
||||
hash_func = sha3_224_buffer;
|
||||
}
|
||||
else if (EQ (algorithm, Qsha3_256))
|
||||
{
|
||||
digest_size = SHA3_256_DIGEST_SIZE;
|
||||
hash_func = sha3_256_buffer;
|
||||
}
|
||||
else if (EQ (algorithm, Qsha3_384))
|
||||
{
|
||||
digest_size = SHA3_384_DIGEST_SIZE;
|
||||
hash_func = sha3_384_buffer;
|
||||
}
|
||||
else if (EQ (algorithm, Qsha3_512))
|
||||
{
|
||||
digest_size = SHA3_512_DIGEST_SIZE;
|
||||
hash_func = sha3_512_buffer;
|
||||
}
|
||||
else
|
||||
error ("Invalid algorithm arg: %s", SDATA (Fsymbol_name (algorithm)));
|
||||
|
||||
@@ -6351,12 +6373,16 @@ anything security-related. See `secure-hash' for alternatives. */)
|
||||
DEFUN ("secure-hash", Fsecure_hash, Ssecure_hash, 2, 5, 0,
|
||||
doc: /* Return the secure hash of OBJECT, a buffer or string.
|
||||
ALGORITHM is a symbol specifying the hash to use:
|
||||
- md5 corresponds to MD5, produces a 32-character signature
|
||||
- sha1 corresponds to SHA-1, produces a 40-character signature
|
||||
- sha224 corresponds to SHA-2 (SHA-224), produces a 56-character signature
|
||||
- sha256 corresponds to SHA-2 (SHA-256), produces a 64-character signature
|
||||
- sha384 corresponds to SHA-2 (SHA-384), produces a 96-character signature
|
||||
- sha512 corresponds to SHA-2 (SHA-512), produces a 128-character signature
|
||||
- md5 corresponds to MD5, produces a 32-character signature
|
||||
- sha1 corresponds to SHA-1, produces a 40-character signature
|
||||
- sha224 corresponds to SHA-2 (SHA-224), produces a 56-character signature
|
||||
- sha256 corresponds to SHA-2 (SHA-256), produces a 64-character signature
|
||||
- sha384 corresponds to SHA-2 (SHA-384), produces a 96-character signature
|
||||
- sha512 corresponds to SHA-2 (SHA-512), produces a 128-character signature
|
||||
- sha3-224 corresponds to SHA-3 (SHA3-224), produces a 56-character signature
|
||||
- sha3-256 corresponds to SHA-3 (SHA3-256), produces a 64-character signature
|
||||
- sha3-384 corresponds to SHA-3 (SHA3-384), produces a 96-character signature
|
||||
- sha3-512 corresponds to SHA-3 (SHA3-512), produces a 128-character signature
|
||||
|
||||
The two optional arguments START and END are positions specifying for
|
||||
which part of OBJECT to compute the hash. If nil or omitted, uses the
|
||||
@@ -6718,12 +6744,16 @@ syms_of_fns (void)
|
||||
/* Crypto and hashing stuff. */
|
||||
DEFSYM (Qiv_auto, "iv-auto");
|
||||
|
||||
DEFSYM (Qmd5, "md5");
|
||||
DEFSYM (Qsha1, "sha1");
|
||||
DEFSYM (Qsha224, "sha224");
|
||||
DEFSYM (Qsha256, "sha256");
|
||||
DEFSYM (Qsha384, "sha384");
|
||||
DEFSYM (Qsha512, "sha512");
|
||||
DEFSYM (Qmd5, "md5");
|
||||
DEFSYM (Qsha1, "sha1");
|
||||
DEFSYM (Qsha224, "sha224");
|
||||
DEFSYM (Qsha256, "sha256");
|
||||
DEFSYM (Qsha384, "sha384");
|
||||
DEFSYM (Qsha512, "sha512");
|
||||
DEFSYM (Qsha3_224, "sha3-224");
|
||||
DEFSYM (Qsha3_256, "sha3-256");
|
||||
DEFSYM (Qsha3_384, "sha3-384");
|
||||
DEFSYM (Qsha3_512, "sha3-512");
|
||||
|
||||
/* Miscellaneous stuff. */
|
||||
|
||||
|
||||
@@ -50,7 +50,12 @@
|
||||
|
||||
(defvar gnutls-tests-internal-macs-upcased
|
||||
(mapcar (lambda (sym) (cons sym (intern (upcase (symbol-name sym)))))
|
||||
(secure-hash-algorithms)))
|
||||
;; Remove the SHA-3 algorithms. Currently, the GNUTLS_MAC_SHA3_*
|
||||
;; macros are reserved, but unimplemented. See:
|
||||
;; <https://www.gnutls.org/manual/html_node/Hash-and-MAC-functions.html>.
|
||||
(seq-remove (lambda (sym)
|
||||
(string-prefix-p "sha3-" (symbol-name sym)))
|
||||
(secure-hash-algorithms))))
|
||||
|
||||
(defvar gnutls-tests-tested-macs
|
||||
(when (gnutls-available-p)
|
||||
|
||||
@@ -1402,6 +1402,18 @@
|
||||
(concat "0a50261ebd1a390fed2bf326f2673c145582a6342d5"
|
||||
"23204973d0219337f81616a8069b012587cf5635f69"
|
||||
"25f1b56c360230c19b273500ee013e030601bf2425")))
|
||||
(should (equal (secure-hash 'sha3-224 "foobar")
|
||||
"1ad852ba147a715fe5a3df39a741fad08186c303c7d21cefb7be763b"))
|
||||
(should (equal (secure-hash 'sha3-256 "foobar")
|
||||
(concat "09234807e4af85f17c66b48ee3bca89d"
|
||||
"ffd1f1233659f9f940a2b17b0b8c6bc5")))
|
||||
(should (equal (secure-hash 'sha3-384 "foobar")
|
||||
(concat "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac"
|
||||
"8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8")))
|
||||
(should (equal (secure-hash 'sha3-512 "foobar")
|
||||
(concat "ff32a30c3af5012ea395827a3e99a13073c3a8d8410"
|
||||
"a708568ff7e6eb85968fccfebaea039bc21411e9d43"
|
||||
"fdb9a851b529b9960ffea8679199781b8f45ca85e2")))
|
||||
;; Test that a call to getrandom returns the right format.
|
||||
;; This does not test randomness; it's merely a format check.
|
||||
(should (string-match "\\`[0-9a-f]\\{128\\}\\'"
|
||||
|
||||
Reference in New Issue
Block a user