--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/*
+ * This is based on
+ * fsprg v0.1 - (seekable) forward-secure pseudorandom generator
+ * Copyright © 2012 B. Poettering
+ * Contact: fsprg@point-at-infinity.org
+ *
+ * OpenSSL port of the original libgcrypt-based implementation.
+ *
+ * See "Practical Secure Logging: Seekable Sequential Key Generators"
+ * by G. A. Marson, B. Poettering for details:
+ *
+ * http://eprint.iacr.org/2013/397
+ */
+
+#include <syslog.h>
+
+#include "alloc-util.h" /* IWYU pragma: keep */
+#include "crypto-util.h"
+#include "fsprg-openssl.h"
+#include "iovec-util.h"
+#include "logarithm.h"
+#include "sparse-endian.h"
+#include "unaligned.h"
+
+#define RND_GEN_P 0x01
+#define RND_GEN_Q 0x02
+#define RND_GEN_X 0x03
+
+/* Suppress a false positive from GCC's -Wstringop-overflow warning. Keep a local helper so the compiler can
+ * propagate the range check within this translation unit. */
+static bool secpar_is_valid(uint16_t secpar) {
+ return
+ secpar % 16 == 0 &&
+ secpar >= 16 &&
+ secpar <= 16384;
+}
+
+bool fsprg_secpar_is_valid(uint16_t secpar) {
+ return secpar_is_valid(secpar);
+}
+
+size_t fsprg_state_size(uint16_t secpar) {
+ assert(secpar_is_valid(secpar));
+
+ /* See comment in parse_state(). */
+ return sizeof(uint16_t) + 2 * secpar / 8 + sizeof(uint64_t);
+}
+
+#if HAVE_OPENSSL
+static int mpi_export(struct iovec *iov, const BIGNUM *x) {
+ assert(iovec_is_set(iov));
+ assert(x);
+
+ if (sym_BN_is_negative(x))
+ return -EINVAL;
+
+ if (sym_BN_num_bytes(x) > (int) iov->iov_len)
+ return -ENOSPC;
+
+ if (sym_BN_bn2binpad(x, iov->iov_base, iov->iov_len) != (int) iov->iov_len)
+ return -EIO;
+
+ return 0;
+}
+
+static int mpi_import(const struct iovec *iov, BIGNUM **ret) {
+ assert(iovec_is_set(iov));
+ assert(ret);
+
+ /* Allocate a new BIGNUM. */
+ _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new();
+ if (!x)
+ return -ENOMEM;
+
+ if (!sym_BN_bin2bn(iov->iov_base, iov->iov_len, x))
+ return -EIO;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+static int det_randomize(const struct iovec *seed, uint32_t idx, struct iovec *iov) {
+ assert(iovec_is_set(seed));
+ assert(iovec_is_valid(iov));
+
+ /* Expand (seed, idx) into a deterministic pseudorandom byte stream. */
+
+ if (!iovec_is_set(iov))
+ return 0;
+
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = sym_EVP_MD_CTX_new();
+ if (!ctx)
+ return -ENOMEM;
+
+ if (sym_EVP_DigestInit_ex(ctx, sym_EVP_sha256(), NULL) <= 0)
+ return -EIO;
+
+ if (sym_EVP_DigestUpdate(ctx, seed->iov_base, seed->iov_len) <= 0)
+ return -EIO;
+
+ if (sym_EVP_DigestUpdate(ctx, (be32_t[]) { htobe32(idx) }, sizeof(be32_t)) <= 0)
+ return -EIO;
+
+ struct iovec v = *iov;
+ for (uint32_t ctr = 0; iovec_is_set(&v); ctr++) {
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *tmp = sym_EVP_MD_CTX_new();
+ if (!tmp)
+ return -ENOMEM;
+
+ if (sym_EVP_MD_CTX_copy_ex(tmp, ctx) <= 0)
+ return -EIO;
+
+ if (sym_EVP_DigestUpdate(tmp, (be32_t[]) { htobe32(ctr) }, sizeof(be32_t)) <= 0)
+ return -EIO;
+
+ unsigned dlen;
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ CLEANUP_ERASE(digest);
+
+ if (sym_EVP_DigestFinal_ex(tmp, digest, &dlen) <= 0)
+ return -EIO;
+
+ size_t n = MIN(v.iov_len, dlen);
+ memcpy(v.iov_base, digest, n);
+ iovec_inc(&v, n);
+ }
+
+ return 0;
+}
+
+/* deterministically generate from seed/idx a prime of length (secpar / 2) bits that is 3 mod 4 */
+static int generate_prime3mod4(
+ uint16_t secpar,
+ const struct iovec *seed,
+ uint32_t idx,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(bn_ctx);
+ assert(ret);
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(iovec_erase) struct iovec iov = IOVEC_ALLOCA(secpar / 2 / 8);
+ r = det_randomize(seed, idx, &iov);
+ if (r < 0)
+ return r;
+
+ uint8_t *buf = iov.iov_base;
+
+ /* Set the upper two bits so that n = pq has the maximum size. */
+ buf[0] |= 0xc0;
+
+ /* Make the candidate congruent to 3 (mod 4). */
+ buf[iov.iov_len - 1] |= 0x03;
+
+ _cleanup_(BN_clear_freep) BIGNUM *p = NULL;
+ r = mpi_import(&iov, &p);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ /* negative: error, 0: composite, 1: prime */
+ r = sym_BN_check_prime(p, bn_ctx, /* cb= */ NULL);
+ if (r < 0)
+ return -EIO;
+ if (r > 0)
+ break;
+
+ /* Increment p by 4. */
+ if (sym_BN_add_word(p, 4) <= 0)
+ return -EIO;
+
+ /* Check if p is still (secpar / 2) bits. */
+ if (sym_BN_num_bits(p) != secpar / 2)
+ return -EOVERFLOW;
+ }
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
+static int generate_keys(
+ uint16_t secpar,
+ const struct iovec *seed,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret_p, /* prime */
+ BIGNUM **ret_q, /* prime */
+ BIGNUM **ret_n) { /* n = p * q */
+
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(bn_ctx);
+ assert(ret_p);
+ assert(ret_q);
+ assert(ret_n);
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(BN_clear_freep) BIGNUM *p = NULL;
+ r = generate_prime3mod4(secpar, seed, RND_GEN_P, bn_ctx, &p);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_clear_freep) BIGNUM *q = NULL;
+ r = generate_prime3mod4(secpar, seed, RND_GEN_Q, bn_ctx, &q);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_clear_freep) BIGNUM *n = sym_BN_secure_new();
+ if (!n)
+ return -ENOMEM;
+
+ if (sym_BN_mul(n, p, q, bn_ctx) <= 0)
+ return -EIO;
+
+ if (sym_BN_num_bits(n) != secpar)
+ return -EIO;
+
+ *ret_p = TAKE_PTR(p);
+ *ret_q = TAKE_PTR(q);
+ *ret_n = TAKE_PTR(n);
+ return 0;
+}
+
+/* deterministically generate from seed/idx a quadratic residue (mod n) */
+static int generate_square(
+ uint16_t secpar,
+ const struct iovec *seed,
+ uint32_t idx,
+ const BIGNUM *n,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(n);
+ assert(bn_ctx);
+ assert(ret);
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(iovec_erase) struct iovec iov = IOVEC_ALLOCA(secpar / 8);
+ r = det_randomize(seed, idx, &iov);
+ if (r < 0)
+ return r;
+
+ *(uint8_t*) iov.iov_base &= 0x7f; /* Clear the upper bit so that we are likely to have x < n. */
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = NULL;
+ r = mpi_import(&iov, &x);
+ if (r < 0)
+ return r;
+
+ /* x < n should always hold for a valid modulus generated by generate_keys(). */
+ if (sym_BN_cmp(x, n) >= 0)
+ return -EINVAL;
+
+ /* x := x^2 mod n */
+ if (sym_BN_mod_sqr(x, x, n, bn_ctx) <= 0)
+ return -EIO;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+/* Compute 2^m mod phi(p), where p is prime and phi(p) = p - 1. */
+static int compute_two_pow_mod_phi(
+ uint64_t m,
+ const BIGNUM *p,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ assert(p);
+ assert(bn_ctx);
+ assert(ret);
+
+ _cleanup_(BN_clear_freep) BIGNUM *phi = sym_BN_secure_new();
+ if (!phi)
+ return -ENOMEM;
+
+ /* phi := p */
+ if (!sym_BN_copy(phi, p))
+ return -EIO;
+
+ /* phi := p - 1 */
+ if (sym_BN_sub_word(phi, 1) <= 0)
+ return -EIO;
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new();
+ if (!x)
+ return -ENOMEM;
+
+ /* x := 1 */
+ if (!sym_BN_one(x))
+ return -EIO;
+
+ if (m == 0) {
+ /* 2^0 mod phi = 1. */
+ *ret = TAKE_PTR(x);
+ return 0;
+ }
+
+ /* Square-and-multiply. Iterate over the bits of m from MSB to LSB. */
+ for (int n = LOG2ULL(m); n >= 0; n--) {
+ if (sym_BN_mod_sqr(x, x, phi, bn_ctx) <= 0) /* x := x^2 mod phi */
+ return -EIO;
+
+ if (m & (UINT64_C(1) << n) &&
+ sym_BN_mod_lshift1_quick(x, x, phi) <= 0) /* x := 2x mod phi */
+ return -EIO;
+ }
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+/* Decompose x ∈ Z_n into (xp, xq) ∈ Z_p × Z_q using the Chinese Remainder Theorem. */
+static int crt_decompose(
+ const BIGNUM *x,
+ const BIGNUM *p,
+ const BIGNUM *q,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret_xp,
+ BIGNUM **ret_xq) {
+
+ assert(x);
+ assert(p);
+ assert(q);
+ assert(bn_ctx);
+ assert(ret_xp);
+ assert(ret_xq);
+
+ _cleanup_(BN_clear_freep) BIGNUM *xp = sym_BN_secure_new();
+ if (!xp)
+ return -ENOMEM;
+
+ _cleanup_(BN_clear_freep) BIGNUM *xq = sym_BN_secure_new();
+ if (!xq)
+ return -ENOMEM;
+
+ if (sym_BN_nnmod(xp, x, p, bn_ctx) <= 0)
+ return -EIO;
+
+ if (sym_BN_nnmod(xq, x, q, bn_ctx) <= 0)
+ return -EIO;
+
+ *ret_xp = TAKE_PTR(xp);
+ *ret_xq = TAKE_PTR(xq);
+ return 0;
+}
+
+/* Compose (xp, xq) ∈ Z_p × Z_q into x ∈ Z_n using the Chinese Remainder Theorem. */
+static int crt_compose(
+ const BIGNUM *xp,
+ const BIGNUM *xq,
+ const BIGNUM *p,
+ const BIGNUM *q,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ assert(xp);
+ assert(xq);
+ assert(p);
+ assert(q);
+ assert(bn_ctx);
+ assert(ret);
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new();
+ if (!x)
+ return -ENOMEM;
+
+ /* x := xq - xp mod q */
+ if (sym_BN_mod_sub(x, xq, xp, q, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Compute p^-1 mod q. */
+ _cleanup_(BN_clear_freep) BIGNUM *p_inv = sym_BN_secure_new();
+ if (!p_inv)
+ return -ENOMEM;
+
+ if (!sym_BN_mod_inverse(p_inv, p, q, bn_ctx))
+ return -EIO;
+
+ /* x := (xq - xp) * p^-1 mod q */
+ if (sym_BN_mod_mul(x, x, p_inv, q, bn_ctx) <= 0)
+ return -EIO;
+
+ /* x := p * ((xq - xp) * p^-1 mod q) */
+ if (sym_BN_mul(x, x, p, bn_ctx) <= 0)
+ return -EIO;
+
+ /* x := p * ((xq - xp) * p^-1 mod q) + xp */
+ if (sym_BN_add(x, x, xp) <= 0)
+ return -EIO;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+static int save_state(
+ uint16_t secpar,
+ const BIGNUM *modulus,
+ const BIGNUM *current,
+ uint64_t epoch,
+ struct iovec *state) {
+
+ int r;
+
+ assert(modulus);
+ assert(current);
+ assert(iovec_is_set(state));
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ if (state->iov_len != fsprg_state_size(secpar))
+ return -EINVAL;
+
+ uint8_t *s = state->iov_base;
+
+ /* header */
+ unaligned_write_be16(s, secpar / 16 - 1);
+ s += sizeof(uint16_t);
+
+ /* modulus */
+ r = mpi_export(&IOVEC_MAKE(s, secpar / 8), modulus);
+ if (r < 0)
+ return r;
+ s += secpar / 8;
+
+ /* current */
+ r = mpi_export(&IOVEC_MAKE(s, secpar / 8), current);
+ if (r < 0)
+ return r;
+ s += secpar / 8;
+
+ /* epoch */
+ unaligned_write_be64(s, epoch);
+ return 0;
+}
+
+static int parse_state(
+ const struct iovec *state,
+ uint16_t *ret_secpar,
+ BIGNUM **ret_modulus,
+ BIGNUM **ret_current,
+ uint64_t *ret_epoch) {
+
+ int r;
+
+ assert(iovec_is_set(state));
+
+ /* The serialized state consists of:
+ * - header: 2 bytes. Encodes secpar / 16 - 1, where 16 <= secpar <= 16384.
+ * - modulus (a.k.a. n): secpar / 8 bytes.
+ * - current (a.k.a. x): secpar / 8 bytes.
+ * - epoch: 8 bytes. */
+
+ if (state->iov_len < sizeof(uint16_t))
+ return -EBADMSG;
+
+ uint16_t header = unaligned_read_be16(state->iov_base);
+ if (header >= 1024)
+ return -EBADMSG;
+
+ uint16_t secpar = 16 * (header + 1);
+ if (state->iov_len != fsprg_state_size(secpar))
+ return -EBADMSG;
+
+ _cleanup_(BN_clear_freep) BIGNUM *modulus = NULL;
+ if (ret_modulus) {
+ r = mpi_import(&IOVEC_MAKE((uint8_t*) state->iov_base + sizeof(uint16_t), secpar / 8), &modulus);
+ if (r < 0)
+ return r;
+ }
+
+ _cleanup_(BN_clear_freep) BIGNUM *current = NULL;
+ if (ret_current) {
+ r = mpi_import(&IOVEC_MAKE((uint8_t*) state->iov_base + sizeof(uint16_t) + secpar / 8, secpar / 8), ¤t);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_secpar)
+ *ret_secpar = secpar;
+ if (ret_modulus)
+ *ret_modulus = TAKE_PTR(modulus);
+ if (ret_current)
+ *ret_current = TAKE_PTR(current);
+ if (ret_epoch)
+ *ret_epoch = unaligned_read_be64((uint8_t*) state->iov_base + sizeof(uint16_t) + 2 * secpar / 8);
+ return 0;
+}
+#endif
+
+int fsprg_generate_state(
+ uint16_t secpar,
+ uint64_t epoch,
+ const struct iovec *seed,
+ struct iovec *state) {
+
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(iovec_is_set(state));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(BN_CTX_freep) BN_CTX *bn_ctx = sym_BN_CTX_secure_new();
+ if (!bn_ctx)
+ return -ENOMEM;
+
+ _cleanup_(BN_clear_freep) BIGNUM *p = NULL, *q = NULL, *n = NULL;
+ r = generate_keys(secpar, seed, bn_ctx, &p, &q, &n);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = NULL;
+ r = generate_square(secpar, seed, RND_GEN_X, n, bn_ctx, &x);
+ if (r < 0)
+ return r;
+
+ if (epoch != 0) {
+ /* Decompose x from Z_n into Z_p × Z_q using CRT. */
+ _cleanup_(BN_clear_freep) BIGNUM *xp = NULL, *xq = NULL;
+ r = crt_decompose(x, p, q, bn_ctx, &xp, &xq);
+ if (r < 0)
+ return r;
+
+ /* Compute 2^epoch (mod phi(p)). */
+ _cleanup_(BN_clear_freep) BIGNUM *kp = NULL;
+ r = compute_two_pow_mod_phi(epoch, p, bn_ctx, &kp);
+ if (r < 0)
+ return r;
+
+ /* Compute 2^epoch (mod phi(q)). */
+ _cleanup_(BN_clear_freep) BIGNUM *kq = NULL;
+ r = compute_two_pow_mod_phi(epoch, q, bn_ctx, &kq);
+ if (r < 0)
+ return r;
+
+ /* Compute x^(2^epoch) (mod p). */
+ if (sym_BN_mod_exp(xp, xp, kp, p, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Compute x^(2^epoch) (mod q). */
+ if (sym_BN_mod_exp(xq, xq, kq, q, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Reconstruct x modulo n from its residues modulo p and q. */
+ BN_clear_freep(&x);
+ r = crt_compose(xp, xq, p, q, bn_ctx, &x);
+ if (r < 0)
+ return r;
+ }
+
+ return save_state(secpar, n, x, epoch, state);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int fsprg_evolve(struct iovec *state) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(state));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ uint16_t secpar;
+ uint64_t epoch;
+ _cleanup_(BN_clear_freep) BIGNUM *n = NULL, *x = NULL;
+ r = parse_state(state, &secpar, &n, &x, &epoch);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_CTX_freep) BN_CTX *bn_ctx = sym_BN_CTX_secure_new();
+ if (!bn_ctx)
+ return -ENOMEM;
+
+ /* Update the current state x := x^2 mod n */
+ if (sym_BN_mod_sqr(x, x, n, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Store the updated current state. */
+ uint8_t *s = (uint8_t*) state->iov_base + sizeof(uint16_t) + secpar / 8;
+ r = mpi_export(&IOVEC_MAKE(s, secpar / 8), x);
+ if (r < 0)
+ return r;
+
+ /* Increment epoch */
+ unaligned_write_be64(s + secpar / 8, epoch + 1);
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int fsprg_get_epoch(const struct iovec *state, uint64_t *ret) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(state));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ return parse_state(
+ state,
+ /* ret_secpar= */ NULL,
+ /* ret_modulus= */ NULL,
+ /* ret_current= */ NULL,
+ ret);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int fsprg_get_key(const struct iovec *state, struct iovec *key) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(state));
+ assert(iovec_is_valid(key));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ uint16_t secpar;
+ r = parse_state(state,
+ /* ret_secpar= */ &secpar,
+ /* ret_modulus= */ NULL,
+ /* ret_current= */ NULL,
+ /* ret_epoch= */ NULL);
+ if (r < 0)
+ return r;
+
+ struct iovec seed = IOVEC_MAKE(
+ (uint8_t*) state->iov_base + sizeof(uint16_t),
+ 2 * secpar / 8 + sizeof(uint64_t));
+
+ return det_randomize(&seed, /* idx= */ 0, key);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
# include <valgrind/valgrind.h>
#endif
+#include "crypto-util.h"
#include "fsprg.h"
+#include "fsprg-openssl.h"
#include "gcrypt-util.h"
#include "iovec-util.h"
#include "journal-def.h"
ASSERT_TRUE(iovec_equal(&state, &states[i]));
}
}
+
+ if (dlopen_libcrypto(LOG_DEBUG) >= 0) {
+ struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
+ state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)),
+ key = IOVEC_ALLOCA(32);
+
+ generate_seed(&seed);
+
+ /* Generate states by fsprg_generate_state() */
+ for (size_t i = 0; i < n_states; i++) {
+ ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, i, &seed, &state));
+
+ /* Verify state */
+ if (i < ELEMENTSOF(expected_state))
+ ASSERT_EQ(iovec_memcmp(&state, &IOVEC_MAKE(expected_state[i], sizeof(expected_state[i]))), 0);
+
+ /* Verify key */
+ ASSERT_OK(fsprg_get_key(&state, &key));
+ if (i < ELEMENTSOF(expected_key))
+ ASSERT_EQ(iovec_memcmp(&key, &IOVEC_MAKE(expected_key[i], sizeof(expected_key[i]))), 0);
+
+ /* Verify epoch */
+ uint64_t epoch;
+ ASSERT_OK(fsprg_get_epoch(&state, &epoch));
+ ASSERT_EQ(epoch, (uint64_t) i);
+
+ if (iovec_is_set(&states[i]))
+ ASSERT_EQ(iovec_memcmp(&state, &states[i]), 0);
+ else
+ ASSERT_NOT_NULL(iovec_memdup(&state, &states[i]));
+ }
+
+ /* Generate state by fsprg_generate_state(0) */
+ ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, 0, &seed, &state));
+ ASSERT_EQ(iovec_memcmp(&state, &states[0]), 0);
+
+ /* Generate states by fsprg_evolve() */
+ for (unsigned i = 1; i < n_states; i++) {
+ ASSERT_OK(fsprg_evolve(&state));
+ ASSERT_EQ(iovec_memcmp(&state, &states[i]), 0);
+ }
+ }
}
TEST(hmac) {
* journal_file_maybe_append_tag(). */
size_t n_tags = 20;
+ struct iovec *tags = new0(struct iovec, n_tags);
+ CLEANUP_ARRAY(tags, n_tags, iovec_array_free);
#if HAVE_GCRYPT
if (initialize_libgcrypt(false) >= 0) {
if (i < ELEMENTSOF(expected_tag))
ASSERT_TRUE(iovec_equal(&tag, &IOVEC_MAKE(expected_tag[i], sizeof(expected_tag[i]))));
+ /* Save tag */
+ ASSERT_NOT_NULL(iovec_memdup(&tag, &tags[i]));
+
/* Increment epoch */
ASSERT_OK(FSPRG_Evolve(state.iov_base));
}
}
#endif
+
+#if HAVE_OPENSSL
+ if (dlopen_libcrypto(LOG_DEBUG) >= 0) {
+ struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
+ state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)),
+ key = IOVEC_ALLOCA(KEY_LENGTH),
+ tag = IOVEC_ALLOCA(TAG_LENGTH);
+
+ generate_seed(&seed);
+
+ /* Calculate initial state */
+ ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, 0, &seed, &state));
+
+ /* Allocate HMAC */
+ _cleanup_(EVP_MAC_freep) EVP_MAC *hmac = ASSERT_NOT_NULL(sym_EVP_MAC_fetch(NULL, "HMAC", NULL));
+ _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *hmac_ctx = ASSERT_NOT_NULL(sym_EVP_MAC_CTX_new(hmac));
+ _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = ASSERT_NOT_NULL(sym_OSSL_PARAM_BLD_new());
+ ASSERT_OK_POSITIVE(sym_OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, "SHA256", 0));
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = ASSERT_NOT_NULL(sym_OSSL_PARAM_BLD_to_param(bld));
+
+ for (size_t i = 0; i < n_tags; i++) {
+ /* Initialize HMAC with fsprg key */
+ ASSERT_OK(fsprg_get_key(&state, &key));
+ ASSERT_OK_POSITIVE(sym_EVP_MAC_init(hmac_ctx, key.iov_base, key.iov_len, params));
+
+ /* Put dummy data */
+ _cleanup_(iovec_done) struct iovec data =
+ IOVEC_MAKE_STRING(ASSERT_NOT_NULL(asprintf_safe("dummy data: %zu, hoge hoge hoge hoge hoge", i)));
+ ASSERT_OK_POSITIVE(sym_EVP_MAC_update(hmac_ctx, data.iov_base, data.iov_len));
+
+ /* Finalize HMAC and generate tag. */
+ size_t len;
+ ASSERT_OK_POSITIVE(sym_EVP_MAC_final(hmac_ctx, tag.iov_base, &len, tag.iov_len));
+ ASSERT_EQ(len, tag.iov_len);
+
+ /* Verify tag */
+ if (i < ELEMENTSOF(expected_tag))
+ ASSERT_TRUE(iovec_equal(&tag, &IOVEC_MAKE(expected_tag[i], sizeof(expected_tag[i]))));
+
+ /* Compare with the tag generated by gcrypt */
+ if (iovec_is_set(&tags[i]))
+ ASSERT_TRUE(iovec_equal(&tag, &tags[i]));
+
+ /* Increment epoch */
+ ASSERT_OK(fsprg_evolve(&state));
+ }
+ }
+#endif
}
static int intro(void) {
- if (DLOPEN_GCRYPT(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0)
+ if (DLOPEN_GCRYPT(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0 &&
+ DLOPEN_LIBCRYPTO(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0)
return EXIT_TEST_SKIP;
return EXIT_SUCCESS;