summaryrefslogtreecommitdiffstats
path: root/src/libcryptobox/cryptobox.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libcryptobox/cryptobox.c1778
1 files changed, 1778 insertions, 0 deletions
diff --git a/src/libcryptobox/cryptobox.c b/src/libcryptobox/cryptobox.c
new file mode 100644
index 0000000..e118c4a
--- /dev/null
+++ b/src/libcryptobox/cryptobox.c
@@ -0,0 +1,1778 @@
+/*-
+ * Copyright 2016 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Workaround for memset_s */
+#ifdef __APPLE__
+#define __STDC_WANT_LIB_EXT1__ 1
+#include <string.h>
+#endif
+
+#include "config.h"
+#include "cryptobox.h"
+#include "platform_config.h"
+#include "chacha20/chacha.h"
+#include "catena/catena.h"
+#include "base64/base64.h"
+#include "ottery.h"
+#include "printf.h"
+#define XXH_INLINE_ALL
+#define XXH_PRIVATE_API
+#include "xxhash.h"
+#define MUM_TARGET_INDEPENDENT_HASH 1 /* For 32/64 bit equal hashes */
+#include "../../contrib/mumhash/mum.h"
+#include "../../contrib/t1ha/t1ha.h"
+#ifdef HAVE_CPUID_H
+#include <cpuid.h>
+#endif
+#ifdef HAVE_OPENSSL
+#include <openssl/opensslv.h>
+/* Openssl >= 1.0.1d is required for GCM verification */
+#if OPENSSL_VERSION_NUMBER >= 0x1000104fL
+#define HAVE_USABLE_OPENSSL 1
+#endif
+#endif
+
+#ifdef HAVE_USABLE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <openssl/rand.h>
+#define CRYPTOBOX_CURVE_NID NID_X9_62_prime256v1
+#endif
+
+#include <signal.h>
+#include <setjmp.h>
+#include <stdalign.h>
+
+#include <sodium.h>
+
+unsigned cpu_config = 0;
+
+static gboolean cryptobox_loaded = FALSE;
+
+static const guchar n0[16] = {0};
+
+#define CRYPTOBOX_ALIGNMENT 16
+#define cryptobox_align_ptr(p, a) \
+ (void *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
+
+static void
+rspamd_cryptobox_cpuid(gint cpu[4], gint info)
+{
+ guint32 __attribute__((unused)) eax, __attribute__((unused)) ecx = 0, __attribute__((unused)) ebx = 0, __attribute__((unused)) edx = 0;
+
+ eax = info;
+#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
+#if defined(__i386__) && defined(__PIC__)
+
+ /* in case of PIC under 32-bit EBX cannot be clobbered */
+
+ __asm__ volatile("movl %%ebx, %%edi \n\t cpuid \n\t xchgl %%ebx, %%edi"
+ : "=D"(ebx),
+ "+a"(eax), "+c"(ecx), "=d"(edx));
+#else
+ __asm__ volatile("cpuid"
+ : "+b"(ebx), "+a"(eax), "+c"(ecx), "=d"(edx));
+#endif
+
+ cpu[0] = eax;
+ cpu[1] = ebx;
+ cpu[2] = ecx;
+ cpu[3] = edx;
+#else
+ memset(cpu, 0, sizeof(gint) * 4);
+#endif
+}
+
+static sig_atomic_t ok = 0;
+static jmp_buf j;
+
+__attribute__((noreturn)) static void
+rspamd_cryptobox_ill_handler(int signo)
+{
+ ok = 0;
+ longjmp(j, -1);
+}
+
+static gboolean
+rspamd_cryptobox_test_instr(gint instr)
+{
+ void (*old_handler)(int);
+ guint32 rd;
+
+#if defined(__GNUC__)
+ ok = 1;
+ old_handler = signal(SIGILL, rspamd_cryptobox_ill_handler);
+
+ if (setjmp(j) != 0) {
+ signal(SIGILL, old_handler);
+
+ return FALSE;
+ }
+
+ switch (instr) {
+#if defined HAVE_SSE2 && defined(__x86_64__)
+ case CPUID_SSE2:
+ __asm__ volatile("psubb %xmm0, %xmm0");
+ break;
+ case CPUID_RDRAND:
+ /* Use byte code here for compatibility */
+ __asm__ volatile(".byte 0x0f,0xc7,0xf0; setc %1"
+ : "=a"(rd), "=qm"(ok)
+ :
+ : "edx");
+ break;
+#endif
+#ifdef HAVE_SSE3
+ case CPUID_SSE3:
+ __asm__ volatile("movshdup %xmm0, %xmm0");
+ break;
+#endif
+#ifdef HAVE_SSSE3
+ case CPUID_SSSE3:
+ __asm__ volatile("pshufb %xmm0, %xmm0");
+ break;
+#endif
+#ifdef HAVE_SSE41
+ case CPUID_SSE41:
+ __asm__ volatile("pcmpeqq %xmm0, %xmm0");
+ break;
+#endif
+#if defined HAVE_SSE42 && defined(__x86_64__)
+ case CPUID_SSE42:
+ __asm__ volatile("pushq %rax\n"
+ "xorq %rax, %rax\n"
+ "crc32 %rax, %rax\n"
+ "popq %rax");
+ break;
+#endif
+#ifdef HAVE_AVX
+ case CPUID_AVX:
+ __asm__ volatile("vpaddq %xmm0, %xmm0, %xmm0");
+ break;
+#endif
+#ifdef HAVE_AVX2
+ case CPUID_AVX2:
+ __asm__ volatile("vpaddq %ymm0, %ymm0, %ymm0");
+ break;
+#endif
+ default:
+ return FALSE;
+ break;
+ }
+
+ signal(SIGILL, old_handler);
+#endif
+
+ (void) rd; /* Silence warning */
+
+ /* We actually never return here if SIGILL has been caught */
+ return ok == 1;
+}
+
+struct rspamd_cryptobox_library_ctx *
+rspamd_cryptobox_init(void)
+{
+ gint cpu[4], nid;
+ const guint32 osxsave_mask = (1 << 27);
+ const guint32 fma_movbe_osxsave_mask = ((1 << 12) | (1 << 22) | (1 << 27));
+ const guint32 avx2_bmi12_mask = (1 << 5) | (1 << 3) | (1 << 8);
+ gulong bit;
+ static struct rspamd_cryptobox_library_ctx *ctx;
+ GString *buf;
+
+ if (cryptobox_loaded) {
+ /* Ignore reload attempts */
+ return ctx;
+ }
+
+ cryptobox_loaded = TRUE;
+ ctx = g_malloc0(sizeof(*ctx));
+
+ rspamd_cryptobox_cpuid(cpu, 0);
+ nid = cpu[0];
+ rspamd_cryptobox_cpuid(cpu, 1);
+
+ if (nid > 1) {
+ if ((cpu[3] & ((guint32) 1 << 26))) {
+ if (rspamd_cryptobox_test_instr(CPUID_SSE2)) {
+ cpu_config |= CPUID_SSE2;
+ }
+ }
+ if ((cpu[2] & ((guint32) 1 << 0))) {
+ if (rspamd_cryptobox_test_instr(CPUID_SSE3)) {
+ cpu_config |= CPUID_SSE3;
+ }
+ }
+ if ((cpu[2] & ((guint32) 1 << 9))) {
+ if (rspamd_cryptobox_test_instr(CPUID_SSSE3)) {
+ cpu_config |= CPUID_SSSE3;
+ }
+ }
+ if ((cpu[2] & ((guint32) 1 << 19))) {
+ if (rspamd_cryptobox_test_instr(CPUID_SSE41)) {
+ cpu_config |= CPUID_SSE41;
+ }
+ }
+ if ((cpu[2] & ((guint32) 1 << 20))) {
+ if (rspamd_cryptobox_test_instr(CPUID_SSE42)) {
+ cpu_config |= CPUID_SSE42;
+ }
+ }
+ if ((cpu[2] & ((guint32) 1 << 30))) {
+ if (rspamd_cryptobox_test_instr(CPUID_RDRAND)) {
+ cpu_config |= CPUID_RDRAND;
+ }
+ }
+
+ /* OSXSAVE */
+ if ((cpu[2] & osxsave_mask) == osxsave_mask) {
+ if ((cpu[2] & ((guint32) 1 << 28))) {
+ if (rspamd_cryptobox_test_instr(CPUID_AVX)) {
+ cpu_config |= CPUID_AVX;
+ }
+ }
+
+ if (nid >= 7 &&
+ (cpu[2] & fma_movbe_osxsave_mask) == fma_movbe_osxsave_mask) {
+ rspamd_cryptobox_cpuid(cpu, 7);
+
+ if ((cpu[1] & avx2_bmi12_mask) == avx2_bmi12_mask) {
+ if (rspamd_cryptobox_test_instr(CPUID_AVX2)) {
+ cpu_config |= CPUID_AVX2;
+ }
+ }
+ }
+ }
+ }
+
+ buf = g_string_new("");
+
+ for (bit = 0x1; bit != 0; bit <<= 1) {
+ if (cpu_config & bit) {
+ switch (bit) {
+ case CPUID_SSE2:
+ rspamd_printf_gstring(buf, "sse2, ");
+ break;
+ case CPUID_SSE3:
+ rspamd_printf_gstring(buf, "sse3, ");
+ break;
+ case CPUID_SSSE3:
+ rspamd_printf_gstring(buf, "ssse3, ");
+ break;
+ case CPUID_SSE41:
+ rspamd_printf_gstring(buf, "sse4.1, ");
+ break;
+ case CPUID_SSE42:
+ rspamd_printf_gstring(buf, "sse4.2, ");
+ break;
+ case CPUID_AVX:
+ rspamd_printf_gstring(buf, "avx, ");
+ break;
+ case CPUID_AVX2:
+ rspamd_printf_gstring(buf, "avx2, ");
+ break;
+ case CPUID_RDRAND:
+ rspamd_printf_gstring(buf, "rdrand, ");
+ break;
+ default:
+ break; /* Silence warning */
+ }
+ }
+ }
+
+ if (buf->len > 2) {
+ /* Trim last chars */
+ g_string_erase(buf, buf->len - 2, 2);
+ }
+
+ ctx->cpu_extensions = buf->str;
+ g_string_free(buf, FALSE);
+ ctx->cpu_config = cpu_config;
+ g_assert(sodium_init() != -1);
+
+ ctx->chacha20_impl = chacha_load();
+ ctx->base64_impl = base64_load();
+#if defined(HAVE_USABLE_OPENSSL) && (OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER))
+ /* Needed for old openssl api, not sure about LibreSSL */
+ ERR_load_EC_strings();
+ ERR_load_RAND_strings();
+ ERR_load_EVP_strings();
+#endif
+
+ return ctx;
+}
+
+void rspamd_cryptobox_deinit(struct rspamd_cryptobox_library_ctx *ctx)
+{
+ if (ctx) {
+ g_free(ctx->cpu_extensions);
+ g_free(ctx);
+ }
+}
+
+void rspamd_cryptobox_keypair(rspamd_pk_t pk, rspamd_sk_t sk,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ ottery_rand_bytes(sk, rspamd_cryptobox_MAX_SKBYTES);
+ sk[0] &= 248;
+ sk[31] &= 127;
+ sk[31] |= 64;
+
+ crypto_scalarmult_base(pk, sk);
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EC_KEY *ec_sec;
+ const BIGNUM *bn_sec;
+
+ const EC_POINT *ec_pub;
+ gsize len;
+
+ ec_sec = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
+ g_assert(ec_sec != NULL);
+ g_assert(EC_KEY_generate_key(ec_sec) != 0);
+
+ bn_sec = EC_KEY_get0_private_key(ec_sec);
+ g_assert(bn_sec != NULL);
+ ec_pub = EC_KEY_get0_public_key(ec_sec);
+ g_assert(ec_pub != NULL);
+#if OPENSSL_VERSION_MAJOR >= 3
+ unsigned char *buf = NULL; /* Thanks openssl for this API (no) */
+ len = EC_POINT_point2buf(EC_KEY_get0_group(ec_sec), ec_pub,
+ POINT_CONVERSION_UNCOMPRESSED, &buf, NULL);
+ g_assert(len <= (gint) rspamd_cryptobox_pk_bytes(mode));
+ memcpy(pk, buf, len);
+ OPENSSL_free(buf);
+#else
+ BIGNUM *bn_pub;
+ bn_pub = EC_POINT_point2bn(EC_KEY_get0_group(ec_sec),
+ ec_pub, POINT_CONVERSION_UNCOMPRESSED, NULL, NULL);
+ len = BN_num_bytes(bn_pub);
+ g_assert(len <= (gint) rspamd_cryptobox_pk_bytes(mode));
+ BN_bn2bin(bn_pub, pk);
+ BN_free(bn_pub);
+#endif
+
+ len = BN_num_bytes(bn_sec);
+ g_assert(len <= (gint) sizeof(rspamd_sk_t));
+ BN_bn2bin(bn_sec, sk);
+
+ EC_KEY_free(ec_sec);
+#endif
+ }
+}
+
+void rspamd_cryptobox_keypair_sig(rspamd_sig_pk_t pk, rspamd_sig_sk_t sk,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_sign_keypair(pk, sk);
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EC_KEY *ec_sec;
+ const BIGNUM *bn_sec;
+ const EC_POINT *ec_pub;
+ gsize len;
+
+ ec_sec = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
+ g_assert(ec_sec != NULL);
+ g_assert(EC_KEY_generate_key(ec_sec) != 0);
+
+ bn_sec = EC_KEY_get0_private_key(ec_sec);
+ g_assert(bn_sec != NULL);
+ ec_pub = EC_KEY_get0_public_key(ec_sec);
+ g_assert(ec_pub != NULL);
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ unsigned char *buf = NULL; /* Thanks openssl for this API (no) */
+ len = EC_POINT_point2buf(EC_KEY_get0_group(ec_sec), ec_pub,
+ POINT_CONVERSION_UNCOMPRESSED, &buf, NULL);
+ g_assert(len <= (gint) rspamd_cryptobox_pk_bytes(mode));
+ memcpy(pk, buf, len);
+ OPENSSL_free(buf);
+#else
+ BIGNUM *bn_pub;
+ bn_pub = EC_POINT_point2bn(EC_KEY_get0_group(ec_sec),
+ ec_pub, POINT_CONVERSION_UNCOMPRESSED, NULL, NULL);
+ len = BN_num_bytes(bn_pub);
+ g_assert(len <= (gint) rspamd_cryptobox_pk_bytes(mode));
+ BN_bn2bin(bn_pub, pk);
+ BN_free(bn_pub);
+#endif
+
+ len = BN_num_bytes(bn_sec);
+ g_assert(len <= (gint) sizeof(rspamd_sk_t));
+ BN_bn2bin(bn_sec, sk);
+ EC_KEY_free(ec_sec);
+#endif
+ }
+}
+
+#if OPENSSL_VERSION_MAJOR >= 3
+/* Compatibility function for OpenSSL 3.0 - thanks for breaking all API one more time */
+EC_POINT *ec_point_bn2point_compat(const EC_GROUP *group,
+ const BIGNUM *bn, EC_POINT *point, BN_CTX *ctx)
+{
+ size_t buf_len = 0;
+ unsigned char *buf;
+ EC_POINT *ret;
+
+ if ((buf_len = BN_num_bytes(bn)) == 0)
+ buf_len = 1;
+ if ((buf = OPENSSL_malloc(buf_len)) == NULL) {
+ return NULL;
+ }
+
+ if (!BN_bn2binpad(bn, buf, buf_len)) {
+ OPENSSL_free(buf);
+ return NULL;
+ }
+
+ if (point == NULL) {
+ if ((ret = EC_POINT_new(group)) == NULL) {
+ OPENSSL_free(buf);
+ return NULL;
+ }
+ }
+ else
+ ret = point;
+
+ if (!EC_POINT_oct2point(group, ret, buf, buf_len, ctx)) {
+ if (ret != point)
+ EC_POINT_clear_free(ret);
+ OPENSSL_free(buf);
+ return NULL;
+ }
+
+ OPENSSL_free(buf);
+ return ret;
+}
+#else
+#define ec_point_bn2point_compat EC_POINT_bn2point
+#endif
+
+void rspamd_cryptobox_nm(rspamd_nm_t nm,
+ const rspamd_pk_t pk, const rspamd_sk_t sk,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ guchar s[32];
+ guchar e[32];
+
+ memcpy(e, sk, 32);
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ if (crypto_scalarmult(s, e, pk) != -1) {
+ hchacha(s, n0, nm, 20);
+ }
+
+ rspamd_explicit_memzero(e, 32);
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EC_KEY *lk;
+ EC_POINT *ec_pub;
+ BIGNUM *bn_pub, *bn_sec;
+ gint len;
+ guchar s[32];
+
+ lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
+ g_assert(lk != NULL);
+
+ bn_pub = BN_bin2bn(pk, rspamd_cryptobox_pk_bytes(mode), NULL);
+ g_assert(bn_pub != NULL);
+ bn_sec = BN_bin2bn(sk, sizeof(rspamd_sk_t), NULL);
+ g_assert(bn_sec != NULL);
+
+ g_assert(EC_KEY_set_private_key(lk, bn_sec) == 1);
+ ec_pub = ec_point_bn2point_compat(EC_KEY_get0_group(lk), bn_pub, NULL, NULL);
+ g_assert(ec_pub != NULL);
+ len = ECDH_compute_key(s, sizeof(s), ec_pub, lk, NULL);
+ g_assert(len == sizeof(s));
+
+ /* Still do hchacha iteration since we are not using SHA1 KDF */
+ hchacha(s, n0, nm, 20);
+
+ EC_KEY_free(lk);
+ EC_POINT_free(ec_pub);
+ BN_free(bn_sec);
+ BN_free(bn_pub);
+#endif
+ }
+}
+
+void rspamd_cryptobox_sign(guchar *sig, unsigned long long *siglen_p,
+ const guchar *m, gsize mlen,
+ const rspamd_sk_t sk,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_sign_detached(sig, siglen_p, m, mlen, sk);
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EC_KEY *lk;
+ BIGNUM *bn_sec;
+ EVP_MD_CTX *sha_ctx;
+ unsigned char h[64];
+ guint diglen = rspamd_cryptobox_signature_bytes(mode);
+
+ /* Prehash */
+ sha_ctx = EVP_MD_CTX_create();
+ g_assert(EVP_DigestInit(sha_ctx, EVP_sha512()) == 1);
+ EVP_DigestUpdate(sha_ctx, m, mlen);
+ EVP_DigestFinal(sha_ctx, h, NULL);
+
+ /* Key setup */
+ lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
+ g_assert(lk != NULL);
+ bn_sec = BN_bin2bn(sk, sizeof(rspamd_sk_t), NULL);
+ g_assert(bn_sec != NULL);
+ g_assert(EC_KEY_set_private_key(lk, bn_sec) == 1);
+
+ /* ECDSA */
+ g_assert(ECDSA_sign(0, h, sizeof(h), sig, &diglen, lk) == 1);
+ g_assert(diglen <= sizeof(rspamd_signature_t));
+
+ if (siglen_p) {
+ *siglen_p = diglen;
+ }
+
+ EC_KEY_free(lk);
+ EVP_MD_CTX_destroy(sha_ctx);
+ BN_free(bn_sec);
+#endif
+ }
+}
+
+bool rspamd_cryptobox_verify(const guchar *sig,
+ gsize siglen,
+ const guchar *m,
+ gsize mlen,
+ const rspamd_pk_t pk,
+ enum rspamd_cryptobox_mode mode)
+{
+ bool ret = false;
+
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ if (siglen == rspamd_cryptobox_signature_bytes(RSPAMD_CRYPTOBOX_MODE_25519)) {
+ ret = (crypto_sign_verify_detached(sig, m, mlen, pk) == 0);
+ }
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EC_KEY *lk;
+ EC_POINT *ec_pub;
+ BIGNUM *bn_pub;
+ EVP_MD_CTX *sha_ctx;
+ unsigned char h[64];
+
+ /* Prehash */
+ sha_ctx = EVP_MD_CTX_create();
+ g_assert(EVP_DigestInit(sha_ctx, EVP_sha512()) == 1);
+ EVP_DigestUpdate(sha_ctx, m, mlen);
+ EVP_DigestFinal(sha_ctx, h, NULL);
+
+ /* Key setup */
+ lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
+ g_assert(lk != NULL);
+ bn_pub = BN_bin2bn(pk, rspamd_cryptobox_pk_bytes(mode), NULL);
+ g_assert(bn_pub != NULL);
+ ec_pub = ec_point_bn2point_compat(EC_KEY_get0_group(lk), bn_pub, NULL, NULL);
+ g_assert(ec_pub != NULL);
+ g_assert(EC_KEY_set_public_key(lk, ec_pub) == 1);
+
+ /* ECDSA */
+ ret = ECDSA_verify(0, h, sizeof(h), sig, siglen, lk) == 1;
+
+ EC_KEY_free(lk);
+ EVP_MD_CTX_destroy(sha_ctx);
+ BN_free(bn_pub);
+ EC_POINT_free(ec_pub);
+#endif
+ }
+
+ return ret;
+}
+
+static gsize
+rspamd_cryptobox_encrypt_ctx_len(enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return sizeof(chacha_state) + CRYPTOBOX_ALIGNMENT;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ return sizeof(EVP_CIPHER_CTX *) + CRYPTOBOX_ALIGNMENT;
+#endif
+ }
+
+ return 0;
+}
+
+static gsize
+rspamd_cryptobox_auth_ctx_len(enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return sizeof(crypto_onetimeauth_state) + RSPAMD_ALIGNOF(crypto_onetimeauth_state);
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ return sizeof(void *);
+#endif
+ }
+
+ return 0;
+}
+
+static void *
+rspamd_cryptobox_encrypt_init(void *enc_ctx, const rspamd_nonce_t nonce,
+ const rspamd_nm_t nm,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ chacha_state *s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ xchacha_init(s,
+ (const chacha_key *) nm,
+ (const chacha_iv24 *) nonce,
+ 20);
+
+ return s;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ memset(s, 0, sizeof(*s));
+ *s = EVP_CIPHER_CTX_new();
+ g_assert(EVP_EncryptInit_ex(*s, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1);
+ g_assert(EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_SET_IVLEN,
+ rspamd_cryptobox_nonce_bytes(mode), NULL) == 1);
+ g_assert(EVP_EncryptInit_ex(*s, NULL, NULL, nm, nonce) == 1);
+
+ return s;
+#endif
+ }
+
+ return NULL;
+}
+
+static void *
+rspamd_cryptobox_auth_init(void *auth_ctx, void *enc_ctx,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_onetimeauth_state *mac_ctx;
+ guchar RSPAMD_ALIGNED(32) subkey[CHACHA_BLOCKBYTES];
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ memset(subkey, 0, sizeof(subkey));
+ chacha_update(enc_ctx, subkey, subkey, sizeof(subkey));
+ crypto_onetimeauth_init(mac_ctx, subkey);
+ rspamd_explicit_memzero(subkey, sizeof(subkey));
+
+ return mac_ctx;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ auth_ctx = enc_ctx;
+
+ return auth_ctx;
+#endif
+ }
+
+ return NULL;
+}
+
+static gboolean
+rspamd_cryptobox_encrypt_update(void *enc_ctx, const guchar *in, gsize inlen,
+ guchar *out, gsize *outlen,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ gsize r;
+ chacha_state *s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+
+ r = chacha_update(s, in, out, inlen);
+
+ if (outlen != NULL) {
+ *outlen = r;
+ }
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = enc_ctx;
+ gint r;
+
+ r = inlen;
+ g_assert(EVP_EncryptUpdate(*s, out, &r, in, inlen) == 1);
+
+ if (outlen) {
+ *outlen = r;
+ }
+
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+static gboolean
+rspamd_cryptobox_auth_update(void *auth_ctx, const guchar *in, gsize inlen,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_onetimeauth_state *mac_ctx;
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_update(mac_ctx, in, inlen);
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+static gsize
+rspamd_cryptobox_encrypt_final(void *enc_ctx, guchar *out, gsize remain,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ chacha_state *s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ return chacha_final(s, out);
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = enc_ctx;
+ gint r = remain;
+
+ g_assert(EVP_EncryptFinal_ex(*s, out, &r) == 1);
+
+ return r;
+#endif
+ }
+
+ return 0;
+}
+
+static gboolean
+rspamd_cryptobox_auth_final(void *auth_ctx, rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_onetimeauth_state *mac_ctx;
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_final(mac_ctx, sig);
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = auth_ctx;
+
+ g_assert(EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_GET_TAG,
+ sizeof(rspamd_mac_t), sig) == 1);
+
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+static void *
+rspamd_cryptobox_decrypt_init(void *enc_ctx, const rspamd_nonce_t nonce,
+ const rspamd_nm_t nm,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+
+ chacha_state *s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ xchacha_init(s,
+ (const chacha_key *) nm,
+ (const chacha_iv24 *) nonce,
+ 20);
+
+ return s;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ memset(s, 0, sizeof(*s));
+ *s = EVP_CIPHER_CTX_new();
+ g_assert(EVP_DecryptInit_ex(*s, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1);
+ g_assert(EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_SET_IVLEN,
+ rspamd_cryptobox_nonce_bytes(mode), NULL) == 1);
+ g_assert(EVP_DecryptInit_ex(*s, NULL, NULL, nm, nonce) == 1);
+
+ return s;
+#endif
+ }
+
+ return NULL;
+}
+
+static void *
+rspamd_cryptobox_auth_verify_init(void *auth_ctx, void *enc_ctx,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_onetimeauth_state *mac_ctx;
+ guchar RSPAMD_ALIGNED(32) subkey[CHACHA_BLOCKBYTES];
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ memset(subkey, 0, sizeof(subkey));
+ chacha_update(enc_ctx, subkey, subkey, sizeof(subkey));
+ crypto_onetimeauth_init(mac_ctx, subkey);
+ rspamd_explicit_memzero(subkey, sizeof(subkey));
+
+ return mac_ctx;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ auth_ctx = enc_ctx;
+
+ return auth_ctx;
+#endif
+ }
+
+ return NULL;
+}
+
+static gboolean
+rspamd_cryptobox_decrypt_update(void *enc_ctx, const guchar *in, gsize inlen,
+ guchar *out, gsize *outlen,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ gsize r;
+ chacha_state *s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ r = chacha_update(s, in, out, inlen);
+
+ if (outlen != NULL) {
+ *outlen = r;
+ }
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = enc_ctx;
+ gint r;
+
+ r = outlen ? *outlen : inlen;
+ g_assert(EVP_DecryptUpdate(*s, out, &r, in, inlen) == 1);
+
+ if (outlen) {
+ *outlen = r;
+ }
+
+ return TRUE;
+#endif
+ }
+}
+
+static gboolean
+rspamd_cryptobox_auth_verify_update(void *auth_ctx,
+ const guchar *in, gsize inlen,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_onetimeauth_state *mac_ctx;
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_update(mac_ctx, in, inlen);
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ /* We do not need to authenticate as a separate process */
+ return TRUE;
+#else
+#endif
+ }
+
+ return FALSE;
+}
+
+static gboolean
+rspamd_cryptobox_decrypt_final(void *enc_ctx, guchar *out, gsize remain,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ chacha_state *s;
+
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ chacha_final(s, out);
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = enc_ctx;
+ gint r = remain;
+
+ if (EVP_DecryptFinal_ex(*s, out, &r) < 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+static gboolean
+rspamd_cryptobox_auth_verify_final(void *auth_ctx, const rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ rspamd_mac_t mac;
+ crypto_onetimeauth_state *mac_ctx;
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_final(mac_ctx, mac);
+
+ if (crypto_verify_16(mac, sig) != 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = auth_ctx;
+
+ if (EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_SET_TAG, 16, (guchar *) sig) != 1) {
+ return FALSE;
+ }
+
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+
+static void
+rspamd_cryptobox_cleanup(void *enc_ctx, void *auth_ctx,
+ enum rspamd_cryptobox_mode mode)
+{
+ if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ crypto_onetimeauth_state *mac_ctx;
+
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ rspamd_explicit_memzero(mac_ctx, sizeof(*mac_ctx));
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ EVP_CIPHER_CTX **s = enc_ctx;
+
+ EVP_CIPHER_CTX_cleanup(*s);
+ EVP_CIPHER_CTX_free(*s);
+#endif
+ }
+}
+
+void rspamd_cryptobox_encrypt_nm_inplace(guchar *data, gsize len,
+ const rspamd_nonce_t nonce,
+ const rspamd_nm_t nm,
+ rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ gsize r;
+ void *enc_ctx, *auth_ctx;
+
+ enc_ctx = g_alloca(rspamd_cryptobox_encrypt_ctx_len(mode));
+ auth_ctx = g_alloca(rspamd_cryptobox_auth_ctx_len(mode));
+
+ enc_ctx = rspamd_cryptobox_encrypt_init(enc_ctx, nonce, nm, mode);
+ auth_ctx = rspamd_cryptobox_auth_init(auth_ctx, enc_ctx, mode);
+
+ rspamd_cryptobox_encrypt_update(enc_ctx, data, len, data, &r, mode);
+ rspamd_cryptobox_encrypt_final(enc_ctx, data + r, len - r, mode);
+
+ rspamd_cryptobox_auth_update(auth_ctx, data, len, mode);
+ rspamd_cryptobox_auth_final(auth_ctx, sig, mode);
+
+ rspamd_cryptobox_cleanup(enc_ctx, auth_ctx, mode);
+}
+
+static void
+rspamd_cryptobox_flush_outbuf(struct rspamd_cryptobox_segment *st,
+ const guchar *buf, gsize len, gsize offset)
+{
+ gsize cpy_len;
+
+ while (len > 0) {
+ cpy_len = MIN(len, st->len - offset);
+ memcpy(st->data + offset, buf, cpy_len);
+ st++;
+ buf += cpy_len;
+ len -= cpy_len;
+ offset = 0;
+ }
+}
+
+void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segments,
+ gsize cnt,
+ const rspamd_nonce_t nonce,
+ const rspamd_nm_t nm, rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ struct rspamd_cryptobox_segment *cur = segments, *start_seg = segments;
+ guchar outbuf[CHACHA_BLOCKBYTES * 16];
+ void *enc_ctx, *auth_ctx;
+ guchar *out, *in;
+ gsize r, remain, inremain, seg_offset;
+
+ enc_ctx = g_alloca(rspamd_cryptobox_encrypt_ctx_len(mode));
+ auth_ctx = g_alloca(rspamd_cryptobox_auth_ctx_len(mode));
+
+ enc_ctx = rspamd_cryptobox_encrypt_init(enc_ctx, nonce, nm, mode);
+ auth_ctx = rspamd_cryptobox_auth_init(auth_ctx, enc_ctx, mode);
+
+ remain = sizeof(outbuf);
+ out = outbuf;
+ inremain = cur->len;
+ seg_offset = 0;
+
+ for (;;) {
+ if (cur - segments == (gint) cnt) {
+ break;
+ }
+
+ if (cur->len <= remain) {
+ memcpy(out, cur->data, cur->len);
+ remain -= cur->len;
+ out += cur->len;
+ cur++;
+
+ if (remain == 0) {
+ rspamd_cryptobox_encrypt_update(enc_ctx, outbuf, sizeof(outbuf),
+ outbuf, NULL, mode);
+ rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf),
+ mode);
+ rspamd_cryptobox_flush_outbuf(start_seg, outbuf,
+ sizeof(outbuf), seg_offset);
+ start_seg = cur;
+ seg_offset = 0;
+ remain = sizeof(outbuf);
+ out = outbuf;
+ }
+ }
+ else {
+ memcpy(out, cur->data, remain);
+ rspamd_cryptobox_encrypt_update(enc_ctx, outbuf, sizeof(outbuf),
+ outbuf, NULL, mode);
+ rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf),
+ mode);
+ rspamd_cryptobox_flush_outbuf(start_seg, outbuf, sizeof(outbuf),
+ seg_offset);
+ seg_offset = 0;
+
+ inremain = cur->len - remain;
+ in = cur->data + remain;
+ out = outbuf;
+ remain = 0;
+ start_seg = cur;
+
+ while (inremain > 0) {
+ if (sizeof(outbuf) <= inremain) {
+ memcpy(outbuf, in, sizeof(outbuf));
+ rspamd_cryptobox_encrypt_update(enc_ctx,
+ outbuf,
+ sizeof(outbuf),
+ outbuf,
+ NULL,
+ mode);
+ rspamd_cryptobox_auth_update(auth_ctx,
+ outbuf,
+ sizeof(outbuf),
+ mode);
+ memcpy(in, outbuf, sizeof(outbuf));
+ in += sizeof(outbuf);
+ inremain -= sizeof(outbuf);
+ remain = sizeof(outbuf);
+ }
+ else {
+ memcpy(outbuf, in, inremain);
+ remain = sizeof(outbuf) - inremain;
+ out = outbuf + inremain;
+ inremain = 0;
+ }
+ }
+
+ seg_offset = cur->len - (sizeof(outbuf) - remain);
+ cur++;
+ }
+ }
+
+ rspamd_cryptobox_encrypt_update(enc_ctx, outbuf, sizeof(outbuf) - remain,
+ outbuf, &r, mode);
+ out = outbuf + r;
+ rspamd_cryptobox_encrypt_final(enc_ctx, out, sizeof(outbuf) - remain - r,
+ mode);
+
+ rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf) - remain,
+ mode);
+ rspamd_cryptobox_auth_final(auth_ctx, sig, mode);
+
+ rspamd_cryptobox_flush_outbuf(start_seg, outbuf, sizeof(outbuf) - remain,
+ seg_offset);
+ rspamd_cryptobox_cleanup(enc_ctx, auth_ctx, mode);
+}
+
+gboolean
+rspamd_cryptobox_decrypt_nm_inplace(guchar *data, gsize len,
+ const rspamd_nonce_t nonce, const rspamd_nm_t nm,
+ const rspamd_mac_t sig, enum rspamd_cryptobox_mode mode)
+{
+ gsize r = 0;
+ gboolean ret = TRUE;
+ void *enc_ctx, *auth_ctx;
+
+ enc_ctx = g_alloca(rspamd_cryptobox_encrypt_ctx_len(mode));
+ auth_ctx = g_alloca(rspamd_cryptobox_auth_ctx_len(mode));
+
+ enc_ctx = rspamd_cryptobox_decrypt_init(enc_ctx, nonce, nm, mode);
+ auth_ctx = rspamd_cryptobox_auth_verify_init(auth_ctx, enc_ctx, mode);
+
+ rspamd_cryptobox_auth_verify_update(auth_ctx, data, len, mode);
+
+ if (!rspamd_cryptobox_auth_verify_final(auth_ctx, sig, mode)) {
+ ret = FALSE;
+ }
+ else {
+ rspamd_cryptobox_decrypt_update(enc_ctx, data, len, data, &r, mode);
+ ret = rspamd_cryptobox_decrypt_final(enc_ctx, data + r, len - r, mode);
+ }
+
+ rspamd_cryptobox_cleanup(enc_ctx, auth_ctx, mode);
+
+ return ret;
+}
+
+gboolean
+rspamd_cryptobox_decrypt_inplace(guchar *data, gsize len,
+ const rspamd_nonce_t nonce,
+ const rspamd_pk_t pk, const rspamd_sk_t sk,
+ const rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ guchar nm[rspamd_cryptobox_MAX_NMBYTES];
+ gboolean ret;
+
+ rspamd_cryptobox_nm(nm, pk, sk, mode);
+ ret = rspamd_cryptobox_decrypt_nm_inplace(data, len, nonce, nm, sig, mode);
+
+ rspamd_explicit_memzero(nm, sizeof(nm));
+
+ return ret;
+}
+
+void rspamd_cryptobox_encrypt_inplace(guchar *data, gsize len,
+ const rspamd_nonce_t nonce,
+ const rspamd_pk_t pk, const rspamd_sk_t sk,
+ rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ guchar nm[rspamd_cryptobox_MAX_NMBYTES];
+
+ rspamd_cryptobox_nm(nm, pk, sk, mode);
+ rspamd_cryptobox_encrypt_nm_inplace(data, len, nonce, nm, sig, mode);
+ rspamd_explicit_memzero(nm, sizeof(nm));
+}
+
+void rspamd_cryptobox_encryptv_inplace(struct rspamd_cryptobox_segment *segments,
+ gsize cnt,
+ const rspamd_nonce_t nonce,
+ const rspamd_pk_t pk, const rspamd_sk_t sk,
+ rspamd_mac_t sig,
+ enum rspamd_cryptobox_mode mode)
+{
+ guchar nm[rspamd_cryptobox_MAX_NMBYTES];
+
+ rspamd_cryptobox_nm(nm, pk, sk, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(segments, cnt, nonce, nm, sig, mode);
+ rspamd_explicit_memzero(nm, sizeof(nm));
+}
+
+
+void rspamd_cryptobox_siphash(unsigned char *out, const unsigned char *in,
+ unsigned long long inlen,
+ const rspamd_sipkey_t k)
+{
+ crypto_shorthash_siphash24(out, in, inlen, k);
+}
+
+/*
+ * Password-Based Key Derivation Function 2 (PKCS #5 v2.0).
+ * Code based on IEEE Std 802.11-2007, Annex H.4.2.
+ */
+static gboolean
+rspamd_cryptobox_pbkdf2(const char *pass, gsize pass_len,
+ const guint8 *salt, gsize salt_len, guint8 *key, gsize key_len,
+ unsigned int rounds)
+{
+ guint8 *asalt, obuf[crypto_generichash_blake2b_BYTES_MAX];
+ guint8 d1[crypto_generichash_blake2b_BYTES_MAX],
+ d2[crypto_generichash_blake2b_BYTES_MAX];
+ unsigned int i, j;
+ unsigned int count;
+ gsize r;
+
+ if (rounds < 1 || key_len == 0) {
+ return FALSE;
+ }
+ if (salt_len == 0 || salt_len > G_MAXSIZE - 4) {
+ return FALSE;
+ }
+
+ asalt = g_malloc(salt_len + 4);
+ memcpy(asalt, salt, salt_len);
+
+ for (count = 1; key_len > 0; count++) {
+ asalt[salt_len + 0] = (count >> 24) & 0xff;
+ asalt[salt_len + 1] = (count >> 16) & 0xff;
+ asalt[salt_len + 2] = (count >> 8) & 0xff;
+ asalt[salt_len + 3] = count & 0xff;
+
+ if (pass_len <= crypto_generichash_blake2b_KEYBYTES_MAX) {
+ crypto_generichash_blake2b(d1, sizeof(d1), asalt, salt_len + 4,
+ pass, pass_len);
+ }
+ else {
+ guint8 k[crypto_generichash_blake2b_BYTES_MAX];
+
+ /*
+ * We use additional blake2 iteration to store large key
+ * XXX: it is not compatible with the original implementation but safe
+ */
+ crypto_generichash_blake2b(k, sizeof(k), pass, pass_len,
+ NULL, 0);
+ crypto_generichash_blake2b(d1, sizeof(d1), asalt, salt_len + 4,
+ k, sizeof(k));
+ }
+
+ memcpy(obuf, d1, sizeof(obuf));
+
+ for (i = 1; i < rounds; i++) {
+ if (pass_len <= crypto_generichash_blake2b_KEYBYTES_MAX) {
+ crypto_generichash_blake2b(d2, sizeof(d2), d1, sizeof(d1),
+ pass, pass_len);
+ }
+ else {
+ guint8 k[crypto_generichash_blake2b_BYTES_MAX];
+
+ /*
+ * We use additional blake2 iteration to store large key
+ * XXX: it is not compatible with the original implementation but safe
+ */
+ crypto_generichash_blake2b(k, sizeof(k), pass, pass_len,
+ NULL, 0);
+ crypto_generichash_blake2b(d2, sizeof(d2), d1, sizeof(d1),
+ k, sizeof(k));
+ }
+
+ memcpy(d1, d2, sizeof(d1));
+
+ for (j = 0; j < sizeof(obuf); j++) {
+ obuf[j] ^= d1[j];
+ }
+ }
+
+ r = MIN(key_len, crypto_generichash_blake2b_BYTES_MAX);
+ memcpy(key, obuf, r);
+ key += r;
+ key_len -= r;
+ }
+
+ rspamd_explicit_memzero(asalt, salt_len + 4);
+ g_free(asalt);
+ rspamd_explicit_memzero(d1, sizeof(d1));
+ rspamd_explicit_memzero(d2, sizeof(d2));
+ rspamd_explicit_memzero(obuf, sizeof(obuf));
+
+ return TRUE;
+}
+
+gboolean
+rspamd_cryptobox_pbkdf(const char *pass, gsize pass_len,
+ const guint8 *salt, gsize salt_len, guint8 *key, gsize key_len,
+ unsigned int complexity, enum rspamd_cryptobox_pbkdf_type type)
+{
+ gboolean ret = FALSE;
+
+ switch (type) {
+ case RSPAMD_CRYPTOBOX_CATENA:
+ if (catena(pass, pass_len, salt, salt_len, "rspamd", 6,
+ 4, complexity, complexity, key_len, key) == 0) {
+ ret = TRUE;
+ }
+ break;
+ case RSPAMD_CRYPTOBOX_PBKDF2:
+ default:
+ ret = rspamd_cryptobox_pbkdf2(pass, pass_len, salt, salt_len, key,
+ key_len, complexity);
+ break;
+ }
+
+ return ret;
+}
+
+guint rspamd_cryptobox_pk_bytes(enum rspamd_cryptobox_mode mode)
+{
+ if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return 32;
+ }
+ else {
+ return 65;
+ }
+}
+
+guint rspamd_cryptobox_pk_sig_bytes(enum rspamd_cryptobox_mode mode)
+{
+ if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return 32;
+ }
+ else {
+ return 65;
+ }
+}
+
+guint rspamd_cryptobox_nonce_bytes(enum rspamd_cryptobox_mode mode)
+{
+ if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return 24;
+ }
+ else {
+ return 16;
+ }
+}
+
+
+guint rspamd_cryptobox_sk_bytes(enum rspamd_cryptobox_mode mode)
+{
+ return 32;
+}
+
+guint rspamd_cryptobox_sk_sig_bytes(enum rspamd_cryptobox_mode mode)
+{
+ if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return 64;
+ }
+ else {
+ return 32;
+ }
+}
+
+guint rspamd_cryptobox_signature_bytes(enum rspamd_cryptobox_mode mode)
+{
+ static guint ssl_keylen;
+
+ if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
+ return 64;
+ }
+ else {
+#ifndef HAVE_USABLE_OPENSSL
+ g_assert(0);
+#else
+ if (ssl_keylen == 0) {
+ EC_KEY *lk;
+ lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
+ ssl_keylen = ECDSA_size(lk);
+ EC_KEY_free(lk);
+ }
+#endif
+ return ssl_keylen;
+ }
+}
+
+guint rspamd_cryptobox_nm_bytes(enum rspamd_cryptobox_mode mode)
+{
+ return 32;
+}
+
+guint rspamd_cryptobox_mac_bytes(enum rspamd_cryptobox_mode mode)
+{
+ return 16;
+}
+
+void rspamd_cryptobox_hash_init(rspamd_cryptobox_hash_state_t *p, const guchar *key, gsize keylen)
+{
+ crypto_generichash_blake2b_state *st = cryptobox_align_ptr(p,
+ RSPAMD_ALIGNOF(crypto_generichash_blake2b_state));
+ crypto_generichash_blake2b_init(st, key, keylen,
+ crypto_generichash_blake2b_BYTES_MAX);
+}
+
+/**
+ * Update hash with data portion
+ */
+void rspamd_cryptobox_hash_update(rspamd_cryptobox_hash_state_t *p, const guchar *data, gsize len)
+{
+ crypto_generichash_blake2b_state *st = cryptobox_align_ptr(p,
+ RSPAMD_ALIGNOF(crypto_generichash_blake2b_state));
+ crypto_generichash_blake2b_update(st, data, len);
+}
+
+/**
+ * Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
+ */
+void rspamd_cryptobox_hash_final(rspamd_cryptobox_hash_state_t *p, guchar *out)
+{
+ crypto_generichash_blake2b_state *st = cryptobox_align_ptr(p,
+ RSPAMD_ALIGNOF(crypto_generichash_blake2b_state));
+ crypto_generichash_blake2b_final(st, out, crypto_generichash_blake2b_BYTES_MAX);
+}
+
+/**
+ * One in all function
+ */
+void rspamd_cryptobox_hash(guchar *out,
+ const guchar *data,
+ gsize len,
+ const guchar *key,
+ gsize keylen)
+{
+ crypto_generichash_blake2b(out, crypto_generichash_blake2b_BYTES_MAX,
+ data, len, key, keylen);
+}
+
+G_STATIC_ASSERT(sizeof(t1ha_context_t) <=
+ sizeof(((rspamd_cryptobox_fast_hash_state_t *) NULL)->opaque));
+G_STATIC_ASSERT(sizeof(struct XXH3_state_s) <=
+ sizeof(((rspamd_cryptobox_fast_hash_state_t *) NULL)->opaque));
+
+
+struct RSPAMD_ALIGNED(16) _mum_iuf {
+ union {
+ gint64 ll;
+ unsigned char b[sizeof(guint64)];
+ } buf;
+ gint64 h;
+ unsigned rem;
+};
+
+rspamd_cryptobox_fast_hash_state_t *
+rspamd_cryptobox_fast_hash_new(void)
+{
+ rspamd_cryptobox_fast_hash_state_t *nst;
+ int ret = posix_memalign((void **) &nst, RSPAMD_ALIGNOF(rspamd_cryptobox_fast_hash_state_t),
+ sizeof(rspamd_cryptobox_fast_hash_state_t));
+
+ if (ret != 0) {
+ abort();
+ }
+
+ return nst;
+}
+
+void rspamd_cryptobox_fast_hash_free(rspamd_cryptobox_fast_hash_state_t *st)
+{
+ free(st);
+}
+
+void rspamd_cryptobox_fast_hash_init(rspamd_cryptobox_fast_hash_state_t *st,
+ guint64 seed)
+{
+ XXH3_state_t *xst = (XXH3_state_t *) st->opaque;
+ st->type = RSPAMD_CRYPTOBOX_XXHASH3;
+ XXH3_INITSTATE(xst);
+ XXH3_64bits_reset_withSeed(xst, seed);
+}
+
+void rspamd_cryptobox_fast_hash_init_specific(rspamd_cryptobox_fast_hash_state_t *st,
+ enum rspamd_cryptobox_fast_hash_type type,
+ guint64 seed)
+{
+ switch (type) {
+ case RSPAMD_CRYPTOBOX_T1HA:
+ case RSPAMD_CRYPTOBOX_HASHFAST:
+ case RSPAMD_CRYPTOBOX_HASHFAST_INDEPENDENT: {
+ t1ha_context_t *rst = (t1ha_context_t *) st->opaque;
+ st->type = RSPAMD_CRYPTOBOX_T1HA;
+ t1ha2_init(rst, seed, 0);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH64: {
+ XXH64_state_t *xst = (XXH64_state_t *) st->opaque;
+ memset(xst, 0, sizeof(*xst));
+ st->type = RSPAMD_CRYPTOBOX_XXHASH64;
+ XXH64_reset(xst, seed);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH32: {
+ XXH32_state_t *xst = (XXH32_state_t *) st->opaque;
+ memset(xst, 0, sizeof(*xst));
+ st->type = RSPAMD_CRYPTOBOX_XXHASH32;
+ XXH32_reset(xst, seed);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH3: {
+ XXH3_state_t *xst = (XXH3_state_t *) st->opaque;
+ XXH3_INITSTATE(xst);
+ st->type = RSPAMD_CRYPTOBOX_XXHASH3;
+ XXH3_64bits_reset_withSeed(xst, seed);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_MUMHASH: {
+ struct _mum_iuf *iuf = (struct _mum_iuf *) st->opaque;
+ st->type = RSPAMD_CRYPTOBOX_MUMHASH;
+ iuf->h = seed;
+ iuf->buf.ll = 0;
+ iuf->rem = 0;
+ break;
+ }
+ }
+}
+
+void rspamd_cryptobox_fast_hash_update(rspamd_cryptobox_fast_hash_state_t *st,
+ const void *data, gsize len)
+{
+ if (st->type == RSPAMD_CRYPTOBOX_T1HA) {
+ t1ha_context_t *rst = (t1ha_context_t *) st->opaque;
+ t1ha2_update(rst, data, len);
+ }
+ else {
+ switch (st->type) {
+ case RSPAMD_CRYPTOBOX_XXHASH64: {
+ XXH64_state_t *xst = (XXH64_state_t *) st->opaque;
+ XXH64_update(xst, data, len);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH32: {
+ XXH32_state_t *xst = (XXH32_state_t *) st->opaque;
+ XXH32_update(xst, data, len);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH3: {
+ XXH3_state_t *xst = (XXH3_state_t *) st->opaque;
+ XXH3_64bits_update(xst, data, len);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_MUMHASH: {
+ struct _mum_iuf *iuf = (struct _mum_iuf *) st->opaque;
+ gsize drem = len;
+ const guchar *p = data;
+
+ if (iuf->rem > 0) {
+ /* Process remainder */
+ if (drem >= iuf->rem) {
+ memcpy(iuf->buf.b + sizeof(iuf->buf.ll) - iuf->rem,
+ p, iuf->rem);
+ drem -= iuf->rem;
+ p += iuf->rem;
+ iuf->h = mum_hash_step(iuf->h, iuf->buf.ll);
+ iuf->rem = 0;
+ }
+ else {
+ memcpy(iuf->buf.b + sizeof(iuf->buf.ll) - iuf->rem, p, drem);
+ iuf->rem -= drem;
+ drem = 0;
+ }
+ }
+
+ while (drem >= sizeof(iuf->buf.ll)) {
+ memcpy(iuf->buf.b, p, sizeof(iuf->buf.ll));
+ iuf->h = mum_hash_step(iuf->h, iuf->buf.ll);
+ drem -= sizeof(iuf->buf.ll);
+ p += sizeof(iuf->buf.ll);
+ }
+
+ /* Leftover */
+ if (drem > 0) {
+ iuf->rem = sizeof(guint64) - drem;
+ iuf->buf.ll = 0;
+ memcpy(iuf->buf.b, p, drem);
+ }
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_T1HA:
+ case RSPAMD_CRYPTOBOX_HASHFAST:
+ case RSPAMD_CRYPTOBOX_HASHFAST_INDEPENDENT: {
+ t1ha_context_t *rst = (t1ha_context_t *) st->opaque;
+ t1ha2_update(rst, data, len);
+ break;
+ }
+ }
+ }
+}
+
+guint64
+rspamd_cryptobox_fast_hash_final(rspamd_cryptobox_fast_hash_state_t *st)
+{
+ guint64 ret;
+
+ if (st->type == RSPAMD_CRYPTOBOX_T1HA) {
+ t1ha_context_t *rst = (t1ha_context_t *) st->opaque;
+
+ return t1ha2_final(rst, NULL);
+ }
+ else {
+ switch (st->type) {
+ case RSPAMD_CRYPTOBOX_XXHASH64: {
+ XXH64_state_t *xst = (XXH64_state_t *) st->opaque;
+ ret = XXH64_digest(xst);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH32: {
+ XXH32_state_t *xst = (XXH32_state_t *) st->opaque;
+ ret = XXH32_digest(xst);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_XXHASH3: {
+ XXH3_state_t *xst = (XXH3_state_t *) st->opaque;
+ ret = XXH3_64bits_digest(xst);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_MUMHASH: {
+ struct _mum_iuf *iuf = (struct _mum_iuf *) st->opaque;
+ iuf->h = mum_hash_step(iuf->h, iuf->buf.ll);
+ ret = mum_hash_finish(iuf->h);
+ break;
+ }
+ case RSPAMD_CRYPTOBOX_T1HA:
+ case RSPAMD_CRYPTOBOX_HASHFAST:
+ case RSPAMD_CRYPTOBOX_HASHFAST_INDEPENDENT: {
+ t1ha_context_t *rst = (t1ha_context_t *) st->opaque;
+
+ ret = t1ha2_final(rst, NULL);
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * One in all function
+ */
+static inline guint64
+rspamd_cryptobox_fast_hash_machdep(const void *data,
+ gsize len, guint64 seed)
+{
+ return XXH3_64bits_withSeed(data, len, seed);
+}
+
+static inline guint64
+rspamd_cryptobox_fast_hash_indep(const void *data,
+ gsize len, guint64 seed)
+{
+ return XXH3_64bits_withSeed(data, len, seed);
+}
+
+guint64
+rspamd_cryptobox_fast_hash(const void *data,
+ gsize len, guint64 seed)
+{
+ return rspamd_cryptobox_fast_hash_machdep(data, len, seed);
+}
+
+guint64
+rspamd_cryptobox_fast_hash_specific(
+ enum rspamd_cryptobox_fast_hash_type type,
+ const void *data,
+ gsize len, guint64 seed)
+{
+ switch (type) {
+ case RSPAMD_CRYPTOBOX_XXHASH32:
+ return XXH32(data, len, seed);
+ case RSPAMD_CRYPTOBOX_XXHASH3:
+ return XXH3_64bits_withSeed(data, len, seed);
+ case RSPAMD_CRYPTOBOX_XXHASH64:
+ return XXH64(data, len, seed);
+ case RSPAMD_CRYPTOBOX_MUMHASH:
+ return mum_hash(data, len, seed);
+ case RSPAMD_CRYPTOBOX_T1HA:
+ return t1ha2_atonce(data, len, seed);
+ case RSPAMD_CRYPTOBOX_HASHFAST_INDEPENDENT:
+ return rspamd_cryptobox_fast_hash_indep(data, len, seed);
+ case RSPAMD_CRYPTOBOX_HASHFAST:
+ default:
+ return rspamd_cryptobox_fast_hash_machdep(data, len, seed);
+ }
+}