Attention is currently required from: flichtenheld.

Hello flichtenheld, 

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/31?usp=email

to look at the new patch set (#17).


Change subject: Allow to use OpenSSL's SIPHASH implementation when available
......................................................................

Allow to use OpenSSL's SIPHASH implementation when available

OpenSSL library is currently slower than the reference implementation
(about 1.5x to 2x depending of the compiler).

The OpenSSL API for using the SIPHASH MAC is different enough from using
normal HMAC or Digest that we already implement that combining them into
one API does not make sense.

SIPHASH is only available on OpenSSL 3.1 and later. We still check for
support on 3.0 and later as the whole API to allow using the SIPHASH alrady
exists in OpenSSL 3.0. Some of the later OpenSSL 3.0.x might get support
for it. Theoretically, a provider can be loaded in OpenSSL 3.0 that
implements SIPHASH.

With OpenSSL's implementation being slower, we currently only use it to
check that our reference implementation and the OpenSSL implementation
yield the same result in unit tests

Change-Id: I09aa27caa1a3aab0d1be6118b26d54a1c1bf7aa0
Signed-off-by: Arne Schwabe <[email protected]>
---
M CMakeLists.txt
M src/openvpn/Makefile.am
M src/openvpn/siphash.h
A src/openvpn/siphash_openssl.c
M src/openvpn/siphash_reference.c
M tests/unit_tests/openvpn/Makefile.am
M tests/unit_tests/openvpn/test_crypto.c
7 files changed, 282 insertions(+), 11 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/31/31/17

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4779d69..db05073 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -562,6 +562,9 @@
     src/openvpn/shaper.h
     src/openvpn/sig.c
     src/openvpn/sig.h
+    src/openvpn/siphash.h
+    src/openvpn/siphash_openssl.c
+    src/openvpn/siphash_reference.c
     src/openvpn/socket.c
     src/openvpn/socket.h
     src/openvpn/socket_util.c
@@ -780,8 +783,12 @@
         src/openvpn/packet_id.c
         src/openvpn/mtu.c
         src/openvpn/mss.c
+        src/openvpn/siphash_reference.c
+        src/openvpn/siphash_openssl.c
         )

+    target_compile_definitions(test_crypto PRIVATE PREFER_OPENSSL_SIPHASH)
+
     target_sources(test_ssl PRIVATE
             tests/unit_tests/openvpn/mock_management.c
             tests/unit_tests/openvpn/mock_ssl_dependencies.c
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index ff8cc54..08c8250 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -128,6 +128,8 @@
        session_id.c session_id.h \
        shaper.c shaper.h \
        sig.c sig.h \
+       siphash_reference.c siphash.h \
+       siphash_openssl.c \
        socket.c socket.h \
        socket_util.c socket_util.h \
        socks.c socks.h \
diff --git a/src/openvpn/siphash.h b/src/openvpn/siphash.h
index ef61110..6d2e134 100644
--- a/src/openvpn/siphash.h
+++ b/src/openvpn/siphash.h
@@ -18,12 +18,67 @@
 #ifndef SIPHASH_H
 #define SIPHASH_H

-#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>

 /* siphash always uses 128-bit keys */
 #define SIPHASH_KEY_SIZE 16

-int siphash(const void *in, size_t inlen, const void *k, uint8_t *out,
-            size_t outlen);
+/* Prototypes for an implementation of SIPHASH in a crypto library */

-#endif
+/**
+ * Calculates SIPHASH using the crypto library function.
+ */
+int
+siphash_cryptolib(void *sip_context, const void *in, size_t inlen,
+                  const void *k, uint8_t *out, size_t outlen);
+
+/**
+ * Calculates SIPHASH using the reference implementation
+ */
+int
+siphash_reference(const void *in, size_t inlen, const void *k,
+                  uint8_t *out, size_t outlen);
+
+/**
+ *
+ * @param hash_size the size of the output hash size
+ * @return initialised context for siphash
+ */
+void *
+siphash_cryptolib_init(size_t hash_size);
+
+/**
+ * Free the siphash context used for the crypto library
+ * @param sip_context
+ */
+void
+siphash_cryptolib_uninit(void *sip_context);
+
+/**
+ * Returns if the crypto library is available (and should be used)
+ *
+ * This returns if there is a crypto library version of Siphash24 is
+ * available and should be used (OpenSSL 3/4 version is quite slow, so
+ * we prefer the reference implementation)
+ *
+ */
+bool
+siphash_cryptolib_available(void *sip_context);
+
+static inline int
+siphash(void *ctx, const void *in, size_t inlen, const void *k,
+        uint8_t *out, size_t outlen)
+{
+    if (siphash_cryptolib_available(ctx) && false)
+    {
+        return siphash_cryptolib(ctx, in, inlen, k, out, outlen);
+    }
+    else
+    {
+        return siphash_reference(in, inlen, k, out, outlen);
+    }
+}
+
+#endif /* ifndef SIPHASH_H */
\ No newline at end of file
diff --git a/src/openvpn/siphash_openssl.c b/src/openvpn/siphash_openssl.c
new file mode 100644
index 0000000..bb42a38
--- /dev/null
+++ b/src/openvpn/siphash_openssl.c
@@ -0,0 +1,150 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2026 OpenVPN Inc <[email protected]>
+ *  Copyright (C) 2026 Arne Schwabe <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include "siphash.h"
+#include "buffer.h"
+
+
+#ifdef ENABLE_CRYPTO_OPENSSL
+#include <openssl/opensslv.h>
+#endif
+
+/* OpenSSL siphash is currently 2-3 times slower than the reference
+ * implementation, so only enable it when PREFER_OPENSSL_SIPHASH is defined
+ * for now */
+#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x30000000L && 
defined(PREFER_OPENSSL_SIPHASH)
+#include <openssl/evp.h>
+#include "crypto_openssl.h"
+
+struct siphash_context
+{
+    EVP_MAC *mac;
+    EVP_MAC_CTX *ctx;
+    size_t size;
+    OSSL_PARAM params[3];
+};
+
+/*
+ *  Computes a SipHash value
+ * in: pointer to input data (read-only)
+ *  inlen: input data length in bytes (any size_t value)
+ * k: pointer to the key data (read-only), must be 16 bytes
+ * out: pointer to output data (write-only), outlen bytes must be allocated
+ *  outlen: length of the output in bytes, must be 8 or 16
+ */
+int
+siphash_cryptolib(void *sip_context, const void *in, const size_t inlen,
+                  const void *k, uint8_t *out, const size_t outlen)
+{
+    struct siphash_context *sip = sip_context;
+
+
+    sip->params[1] = OSSL_PARAM_construct_octet_string("key", (void *)k,
+                                                       SIPHASH_KEY_SIZE);
+    if (!EVP_MAC_init(sip->ctx, NULL, 0, sip->params))
+    {
+        crypto_msg(M_FATAL, "EVP_MAC_init failed");
+    }
+    EVP_MAC_update(sip->ctx, in, inlen);
+
+    size_t outl = 0;
+    EVP_MAC_final(sip->ctx, out, &outl, outlen);
+    return 0;
+}
+
+void *
+siphash_cryptolib_init(size_t hash_size)
+{
+    struct siphash_context *sip;
+    ALLOC_OBJ(sip, struct siphash_context);
+
+    sip->mac = EVP_MAC_fetch(NULL, "SIPHASH", NULL);
+    if (!sip->mac)
+    {
+        /* Our OpenSSL library does not support SIPHASH */
+        return sip;
+    }
+    sip->ctx = EVP_MAC_CTX_new(sip->mac);
+
+    /* OpenSSL will truly hold a pointer to an int in that parameter */
+    sip->size = hash_size;
+    sip->params[0] = OSSL_PARAM_construct_size_t("size", &sip->size);
+    /* params[1] will hold the key that changes which each invocation */
+    sip->params[2] = OSSL_PARAM_construct_end();
+    return sip;
+}
+
+bool
+siphash_cryptolib_available(void *sip_context)
+{
+    struct siphash_context *sip = sip_context;
+
+    return (bool)(sip->mac);
+}
+
+void
+siphash_cryptolib_uninit(void *sip_context)
+{
+    struct siphash_context *sip = sip_context;
+    EVP_MAC_CTX_free(sip->ctx);
+    EVP_MAC_free(sip->mac);
+    free(sip_context);
+}
+
+#else
+/* for now, we only have one implementation of SIPHASH in a libray, so put the
+ * dummy functions also here */
+int
+siphash_cryptolib(void *sip_context, const void *in, const size_t inlen,
+                  const void *k, uint8_t *out, const size_t outlen)
+{
+    return -1;
+}
+
+bool
+siphash_cryptolib_available(void *sip_context)
+{
+    return false;
+}
+
+void *
+siphash_cryptolib_init(size_t hash_size)
+{
+    return NULL;
+}
+
+void
+siphash_cryptolib_uninit(void *sip_context)
+{
+}
+
+
+#endif /* if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 
0x30000000L */
diff --git a/src/openvpn/siphash_reference.c b/src/openvpn/siphash_reference.c
index b21a86e..a06599f 100644
--- a/src/openvpn/siphash_reference.c
+++ b/src/openvpn/siphash_reference.c
@@ -100,8 +100,8 @@
  *  outlen: length of the output in bytes, must be 8 or 16
  */
 int
-siphash(const void *in, const size_t inlen, const void *k, uint8_t *out,
-        const size_t outlen)
+siphash_reference(const void *in, const size_t inlen, const void *k,
+                  uint8_t *out, const size_t outlen)
 {
     const unsigned char *ni = (const unsigned char *)in;
     const unsigned char *kk = (const unsigned char *)k;
diff --git a/tests/unit_tests/openvpn/Makefile.am 
b/tests/unit_tests/openvpn/Makefile.am
index 1128eb4..4ed113a 100644
--- a/tests/unit_tests/openvpn/Makefile.am
+++ b/tests/unit_tests/openvpn/Makefile.am
@@ -84,7 +84,9 @@
        $(top_srcdir)/src/openvpn/platform.c \
        $(top_srcdir)/src/openvpn/mtu.c \
        $(top_srcdir)/src/openvpn/win32-util.c \
-       $(top_srcdir)/src/openvpn/mss.c
+       $(top_srcdir)/src/openvpn/mss.c \
+       $(top_srcdir)/src/openvpn/siphash_openssl.c \
+       $(top_srcdir)/src/openvpn/siphash_reference.c

 dhcp_testdriver_CFLAGS  = -I$(top_srcdir)/src/openvpn 
-I$(top_srcdir)/src/compat @TEST_CFLAGS@ -DDHCP_UNIT_TEST
 dhcp_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(top_srcdir)/src/openvpn
@@ -97,6 +99,7 @@

 ssl_testdriver_CFLAGS  = \
        -I$(top_srcdir)/include -I$(top_srcdir)/src/compat 
-I$(top_srcdir)/src/openvpn \
+       -DPREFER_OPENSSL_SIPHASH \
        @TEST_CFLAGS@
 ssl_testdriver_LDFLAGS = @TEST_LDFLAGS@  $(OPTIONAL_CRYPTO_LIBS)
 ssl_testdriver_SOURCES = test_ssl.c \
diff --git a/tests/unit_tests/openvpn/test_crypto.c 
b/tests/unit_tests/openvpn/test_crypto.c
index cb4eaa2..e528f26 100644
--- a/tests/unit_tests/openvpn/test_crypto.c
+++ b/tests/unit_tests/openvpn/test_crypto.c
@@ -31,12 +31,14 @@
 #include <stdarg.h>
 #include <string.h>
 #include <setjmp.h>
+#include <inttypes.h>
 #include <cmocka.h>

 #include "crypto.h"
 #include "crypto_epoch.h"
 #include "options.h"
 #include "ssl_backend.h"
+#include "siphash.h"

 #include "mss.h"
 #include "test_common.h"
@@ -923,6 +925,59 @@
     assert_memory_equal(key_parameters.hmac, exp_impl_iv, sizeof(exp_impl_iv));
 }

+/* Use a define here since some c compilers don't like array initialisation
+ * with an integer */
+#define UT_SIPHASH_HASH_SIZE 16
+
+static void
+test_siphash(void **state)
+{
+    const char *message = "Look behind you, a Three-Headed Monkey!";
+
+    uint8_t out[UT_SIPHASH_HASH_SIZE] = { 0 };
+    const uint8_t key[SIPHASH_KEY_SIZE] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 
};
+
+    siphash_reference(message, strlen(message), key, out, 
UT_SIPHASH_HASH_SIZE);
+
+    const uint8_t expected_out[UT_SIPHASH_HASH_SIZE] = { 0x3e, 0xea, 0x95, 
0xb2, 0x6d, 0x5c, 0x4e, 0xfa,
+                                                         0x20, 0x47, 0x65, 
0x7e, 0xdd, 0xcd, 0x62, 0x51 };
+    assert_memory_equal(out, expected_out, UT_SIPHASH_HASH_SIZE);
+}
+
+static void
+test_siphash_openssl(void **state)
+{
+    void *sipctx = siphash_cryptolib_init(UT_SIPHASH_HASH_SIZE);
+
+    if (!siphash_cryptolib_available(sipctx))
+    {
+        siphash_cryptolib_uninit(sipctx);
+        skip();
+    }
+
+    const char *message = "Look behind you, a Three-Headed Monkey!";
+
+    uint8_t out[UT_SIPHASH_HASH_SIZE] = { 0 };
+    const uint8_t key[SIPHASH_KEY_SIZE] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 
};
+
+
+    const uint8_t expected_out[UT_SIPHASH_HASH_SIZE] = { 0x3e, 0xea, 0x95, 
0xb2, 0x6d, 0x5c, 0x4e, 0xfa,
+                                                         0x20, 0x47, 0x65, 
0x7e, 0xdd, 0xcd, 0x62, 0x51 };
+
+
+    siphash_cryptolib(sipctx, message, strlen(message), key, out,
+                      UT_SIPHASH_HASH_SIZE);
+    assert_memory_equal(out, expected_out, UT_SIPHASH_HASH_SIZE);
+
+    /* check that calling the function twice is safe */
+    siphash_cryptolib(sipctx, message, strlen(message), key, out,
+                      UT_SIPHASH_HASH_SIZE);
+    assert_memory_equal(out, expected_out, UT_SIPHASH_HASH_SIZE);
+
+    siphash_cryptolib_uninit(sipctx);
+}
+
+
 int
 main(void)
 {
@@ -957,10 +1012,9 @@
         
cmocka_unit_test_prestate_setup_teardown(crypto_test_epoch_key_overflow,
                                                  crypto_test_epoch_setup,
                                                  crypto_test_epoch_teardown, 
&prestate_num32),
-        cmocka_unit_test_prestate_setup_teardown(crypto_test_epoch_edge,
-                                                 crypto_test_epoch_setup,
-                                                 crypto_test_epoch_teardown, 
&prestate_num13),
-        cmocka_unit_test(epoch_test_derive_data_key)
+        cmocka_unit_test(epoch_test_derive_data_key),
+        cmocka_unit_test(test_siphash),
+        cmocka_unit_test(test_siphash_openssl)
     };

     return cmocka_run_group_tests_name("crypto tests", tests, NULL, NULL);

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/31?usp=email
To unsubscribe, or for help writing mail filters, visit 
http://gerrit.openvpn.net/settings?usp=email

Gerrit-MessageType: newpatchset
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I09aa27caa1a3aab0d1be6118b26d54a1c1bf7aa0
Gerrit-Change-Number: 31
Gerrit-PatchSet: 17
Gerrit-Owner: plaisthos <[email protected]>
Gerrit-Reviewer: flichtenheld <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: flichtenheld <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to