packages feed

hashes-0.3.0: cbits/keccak.c

/*
 * Copyright: Copyright © 2021-2024 Lars Kuhtz <lakuhtz@gmail.com>
 * License: MIT
 * Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
 */

#include <openssl/evp.h>
#include "keccak.h"

/* *************************************************************************** */
/* Legacy support for Keccak for OpenSSL prior to version 3.2
 *
 * Support for Keccak-224, Keccak-256, Keccak-384, and Keccak-512 was added
 * in OpenSSL version 3.2.
 *
 * History (cf. https://openssl.org/policies/releasestrat.html)
 *
 * - OpenSSL 1.1: Support ended 2023-09-11.
 * - OpenSSL 3.0: Support ends 2026-09-07 (LTS).
 * - OpenSSL 3.1: Support ends 2025-03-14.
 * - OpenSSL 3.2: Native Keccak support added.
 *
 * This file also adds supports for those digests for OpenSSL versions 1.1
 * onward.
 *
 * Keccak differs from NIST standardized SHA3 by using a different padding
 * value. This implementation uses low-level internals of the SHA3
 * implementation for overwriting the padding byte.
 */

/* *************************************************************************** */
/* OpenSSL 1.1 and OpenSSL 3.0 */

/* The computation of the magic offset is based on the keccak_st structure in
 * OpenSSL-1.1 and OpenSSL-3.0
 *
 * Assuming conventional alignment, the bytes offset is
 *
 * sizeof(uint64_t) * 25 + size_of(size_t) * 3 + (1600 / 8 - 32)
 *
 * On a 64bit platform this number is 392
 *
 * // struct keccak_st {
 * //     uint64_t A[5][5];
 * //     size_t block_size;
 * //     size_t md_size;
 * //     size_t bufsz;
 * //     unsigned char buf[KECCAK1600_WIDTH / 8 - 32];
 * //     unsigned char pad;
 * // };
 *
 * For OpenSSL 1.1 this context is stored in EVP_MD_CTX as data and is accessed
 * via EVP_CTX_MD_md_data().
 *
 * For OpenSSL 3.0 it is stored in EVP_MD_CTX in the algctx field. The structure
 * is a field of pointers and algctx is the 8th pointer.
 *
 * // struct evp_md_ctx_st {
 * //   const EVP_MD *reqdigest;
 * //   const EVP_MD *digest;
 * //   ENGINE *engine;
 * //   unsigned long flags;
 * //   void *md_data;
 * //   EVP_PKEY_CTX *pctx;
 * //   int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
 * //
 * //   // Opaque ctx returned from a providers digest algorithm implementation
 * //   // OSSL_FUNC_digest_newctx()
 * //   //
 * //   void *algctx;
 * //   EVP_MD *fetched_digest;
 * // }
 */

#define PAD_BYTE_OFFSET (25 * sizeof(uint64_t) + 3 * sizeof(size_t) + 1600/8 - 32)

/* OPENSSL 3.2 */
#if OPENSSL_VERSION_NUMBER >= 0x30200000L

/* Keccak is supported directly in OpenSSL >= 2.3. No legacy implementation is
 * provided in this case.
 */

/* OPENSSL 3.0 (use with algorithm name "SHA3") */
#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
#define GET_CTX(ctx) (*(((uint8_t **) ctx) + 7))
#define SET_PAD_BYTE (((uint8_t *) GET_CTX(ctx))[PAD_BYTE_OFFSET] = 0x01)

/* OPENSSL 1.1 (use with algorithm name "SHA3") */
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
#define GET_CTX(ctx) ((uint8_t *) EVP_MD_CTX_md_data(ctx))
#define SET_PAD_BYTE (GET_CTX(ctx)[PAD_BYTE_OFFSET] = 0x01)

#else
#error "Unsupported OpenSSL version. Please install OpenSSL >= 1.1.0"

#endif

/* *************************************************************************** */
/* Implementation */

#if OPENSSL_VERSION_NUMBER < 0x30200000L
int keccak_EVP_DigestInit_ex(EVP_MD_CTX *ctx, EVP_MD *md) {
    int ok = 1;
    CHECKED(EVP_DigestInit_ex(ctx, md, NULL));
    SET_PAD_BYTE;
finally:
    return ok;
}
#endif