From patchwork Sat Dec 21 22:24:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 4010 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:998b:b0:5e7:b9eb:58e8 with SMTP id d11csp3201472mav; Sat, 21 Dec 2024 14:24:20 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCUTtW609LfrhWqRanm1+2ub3w589RRg9K0pqNUHlJq4iD04uk3Yj/fJmXhdEeUcHQ9VE/cKqYKc4T4=@openvpn.net X-Google-Smtp-Source: AGHT+IFjsYwQjQieQSNpn/4N2q/OhShvPAWjCRhe1wCF8/n/qz3Rju+BtEInfJlncMLfzpFXCVl2 X-Received: by 2002:a05:6820:2718:b0:5e1:d741:6f04 with SMTP id 006d021491bc7-5f62e77186dmr4874541eaf.3.1734819859135; Sat, 21 Dec 2024 14:24:19 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1734819859; cv=none; d=google.com; s=arc-20240605; b=kB6CqEnPcAsylG/mIQfoRHYHTAzqMVdgkgI2+p3Je/GrJrdgkRbz75stTPh7ev9Vw0 kn90chHq4SgDoPhuKfHf3w/PTjIVFPCLddRc2pj69tge1tKQ2Fw2VJ/p6DwU/ZX5X4FW g8vjmeMCuTcaOar9qwtBsaP8COd/tuz4mpoXUki454PK32jhDo0aW2PxUiDbUTSovFB6 mhCYvFepLk7SBugPXQ/oIHPDp7m/vLQgSlQ03f8pyiwz4/9yAv39XcG0Pfd+Xp78NReJ UHAX+UIkLG3sWVZ7fDyjJsqluXXttqoqqceaYYIWEtaEv4xlM9M7uxZiCxRB9zaC1Cni Qq6g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:content-transfer-encoding:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature; bh=85hYEODY2UXn8jD4OM3ge01mN6H7lRAQw9YYKPQLLxw=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=akw3n5lazAc8vrFvRHdFai2sgaUTBmKslYF5DB9DQpJJSDHEGnO6lxQuNM08bta0l+ nTKGJcUdrn5AUWNQhMVa2e8IMxe62cz+46h1vnGXV9Few84qQke4LPFyPF/Ub364HgkX Dp9Coyf6yh0vna/PDmAS4oTjmtAGTmImRa2WdQbeTT0mnPCOLCJ5Fngqlzmpy/CTT3+H SuxRSTFR1cYcaws1WXSvY2M/UXRlmx0oa2hCGxyBxH4FYjFLqYIcRj7BKd1ANV0XcTWT Qql6dBtd3BT2GLT4QoFf+64B/+2AeAKpa0Drf64yl3POV2rkLirv3LuxWMAJtoCVMhRQ FFfQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=mDb44hgi; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b="QQJ/gjBJ"; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 006d021491bc7-5f4db62c70asi3971968eaf.51.2024.12.21.14.24.18 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 21 Dec 2024 14:24:19 -0800 (PST) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=mDb44hgi; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b="QQJ/gjBJ"; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1tP7tO-0006IM-8u; Sat, 21 Dec 2024 22:24:15 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1tP7tM-0006IG-Th for openvpn-devel@lists.sourceforge.net; Sat, 21 Dec 2024 22:24:13 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=CtOQ5ZKBd7fCjLYjnB4eCSdkwpzbuRmQJ8KHm+U9lIY=; b=mDb44hgipdPLKjtoYuhvjHn396 g0TIjxr+gLT3WBaveEOpG5pVey4rUUkGzMgpQqwIqBIovGUDDnrWUwp3EbTZz2ZKc6rkURYL6yVcB X/EHfLrhT3R9ECwHdYY3ZKv+IWzBEM/7pMFwGUpZQeKltkc8YVZ1lGGfk+/0lJnWwce4=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=CtOQ5ZKBd7fCjLYjnB4eCSdkwpzbuRmQJ8KHm+U9lIY=; b=QQJ/gjBJSs+bGJqr3ajWGteZJS CZDMO0C3/SmJE6iyHIi0bK/kdrL04iIUQl5znADHOPCOzZ39VRqx09ZjMW6IFVri8uNQBg/LzfNOH vKD2arqTOxVMjUraz8wcgJb5gCgRjHO8yk25g5ZhTJqWep+5rfQAkXzM1wNypZbR8I4U=; Received: from dhcp-174.greenie.muc.de ([193.149.48.174] helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1tP7tL-0005Fy-St for openvpn-devel@lists.sourceforge.net; Sat, 21 Dec 2024 22:24:13 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.17.1.9/8.17.1.9) with ESMTP id 4BLMO5oA010276 for ; Sat, 21 Dec 2024 23:24:05 +0100 Received: (from gert@localhost) by blue.greenie.muc.de (8.17.1.9/8.17.1.9/Submit) id 4BLMO5i0010275 for openvpn-devel@lists.sourceforge.net; Sat, 21 Dec 2024 23:24:05 +0100 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Sat, 21 Dec 2024 23:24:04 +0100 Message-ID: <20241221222404.10266-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 0.0 (/) X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Arne Schwabe Use crypto_epoch.c/h for the new functions since they are linked to the epoch key usage in OpenVPN. Change-Id: I3a1c6561f4d9a69e2a441d49dff620b4258a1bcc Signed-off-by: Arne Schwabe Acked-by: Gert Doering --- Content analysis details: (0.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [193.149.48.174 listed in bl.score.senderscore.com] 0.0 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. [193.149.48.174 listed in sa-trusted.bondedsender.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1tP7tL-0005Fy-St Subject: [Openvpn-devel] [PATCH v9] Implement HKDF expand function based on RFC 8446 X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: =?utf-8?q?1819090468563251124?= X-GMAIL-MSGID: =?utf-8?q?1819090468563251124?= From: Arne Schwabe Use crypto_epoch.c/h for the new functions since they are linked to the epoch key usage in OpenVPN. Change-Id: I3a1c6561f4d9a69e2a441d49dff620b4258a1bcc Signed-off-by: Arne Schwabe Acked-by: Gert Doering --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/798 This mail reflects revision 9 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering diff --git a/CMakeLists.txt b/CMakeLists.txt index ca58cd7..61f0cc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,6 +387,8 @@ src/openvpn/crypto.c src/openvpn/crypto.h src/openvpn/crypto_backend.h + src/openvpn/crypto_epoch.c + src/openvpn/crypto_epoch.h src/openvpn/crypto_openssl.c src/openvpn/crypto_openssl.h src/openvpn/crypto_mbedtls.c @@ -715,6 +717,7 @@ target_sources(test_crypto PRIVATE src/openvpn/crypto_mbedtls.c src/openvpn/crypto_openssl.c + src/openvpn/crypto_epoch.c src/openvpn/crypto.c src/openvpn/otime.c src/openvpn/packet_id.c diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index ecb2bcf..d6d6592 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -53,6 +53,7 @@ crypto.c crypto.h crypto_backend.h \ crypto_openssl.c crypto_openssl.h \ crypto_mbedtls.c crypto_mbedtls.h \ + crypto_epoch.c crypto_epoch.h \ dco.c dco.h dco_internal.h \ dco_freebsd.c dco_freebsd.h \ dco_linux.c dco_linux.h \ diff --git a/src/openvpn/crypto_epoch.c b/src/openvpn/crypto_epoch.c new file mode 100644 index 0000000..667e12a6 --- /dev/null +++ b/src/openvpn/crypto_epoch.c @@ -0,0 +1,114 @@ +/* + * 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) 2024 OpenVPN Inc + * Copyright (C) 2024 Arne Schwabe + * + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "crypto_backend.h" +#include "buffer.h" +#include "integer.h" + +void +ovpn_hkdf_expand(const uint8_t *secret, + const uint8_t *info, int info_len, + uint8_t *out, int out_len) +{ + hmac_ctx_t *hmac_ctx = hmac_ctx_new(); + hmac_ctx_init(hmac_ctx, secret, "SHA256"); + + const int digest_size = SHA256_DIGEST_LENGTH; + + /* T(0) = empty string */ + uint8_t t_prev[SHA256_DIGEST_LENGTH]; + int t_prev_len = 0; + + for (uint8_t block = 1; (block - 1) * digest_size < out_len; block++) + { + hmac_ctx_reset(hmac_ctx); + + /* calculate T(block) */ + hmac_ctx_update(hmac_ctx, t_prev, t_prev_len); + hmac_ctx_update(hmac_ctx, info, info_len); + hmac_ctx_update(hmac_ctx, &block, 1); + hmac_ctx_final(hmac_ctx, t_prev); + t_prev_len = digest_size; + + /* Copy a full hmac output or remaining bytes */ + int out_offset = (block - 1) * digest_size; + int copylen = min_int(digest_size, out_len - out_offset); + + memcpy(out + out_offset, t_prev, copylen); + } + hmac_ctx_cleanup(hmac_ctx); + hmac_ctx_free(hmac_ctx); +} + +bool +ovpn_expand_label(const uint8_t *secret, size_t secret_len, + const uint8_t *label, size_t label_len, + const uint8_t *context, size_t context_len, + uint8_t *out, uint16_t out_len) +{ + if (secret_len != 32 || label_len > 250 || context_len > 255 + || label_len < 1) + { + /* Our current implementation is not a general purpose one + * and assumes that the secret size matches the size of the + * hash (SHA256) key. Also label length and context length + * need need to be in range */ + return false; + } + + struct gc_arena gc = gc_new(); + /* 2 byte for the outlen encoded as uint16, 5 bytes for "ovpn ", + * 1 byte for context len byte and 1 byte for label len byte */ + const uint8_t *label_prefix = (const uint8_t *) ("ovpn "); + int prefix_len = 5; + + int hkdf_label_len = 2 + prefix_len + 1 + label_len + 1 + context_len; + struct buffer hkdf_label = alloc_buf_gc(hkdf_label_len, &gc); + + buf_write_u16(&hkdf_label, out_len); + buf_write_u8(&hkdf_label, prefix_len + label_len); + buf_write(&hkdf_label, label_prefix, prefix_len); + buf_write(&hkdf_label, label, label_len); + + buf_write_u8(&hkdf_label, context_len); + if (context_len > 0) + { + buf_write(&hkdf_label, context, context_len); + } + + ASSERT(buf_len(&hkdf_label) == hkdf_label_len); + + ovpn_hkdf_expand(secret, buf_bptr(&hkdf_label), + buf_len(&hkdf_label), out, out_len); + + gc_free(&gc); + return true; +} diff --git a/src/openvpn/crypto_epoch.h b/src/openvpn/crypto_epoch.h new file mode 100644 index 0000000..e5d0e5d --- /dev/null +++ b/src/openvpn/crypto_epoch.h @@ -0,0 +1,70 @@ +/* + * 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) 2024 OpenVPN Inc + * Copyright (C) 2024 Arne Schwabe + * + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef CRYPTO_EPOCH_H +#define CRYPTO_EPOCH_H + +/** + * Implementation of the RFC5869 HKDF-Expand function with the following + * restrictions + * + * - secret is assumed to be always 32 bytes + * - HASH is always SHA256 + * + * @param secret the input keying material (HMAC key) + * @param info context and application specific information + * @param info_len length of the info string + * @param out output keying material + * @param out_len length of output keying material + */ +void +ovpn_hkdf_expand(const uint8_t *secret, + const uint8_t *info, int info_len, + uint8_t *out, int out_len); + +/** + * Variant of the RFC 8446 TLS 1.3 HKDF-Expand-Label function with the + * following differences/restrictions: + * - secret must 32 bytes in length + * - label prefix is "ovpn " instead of "tls13 " + * - HASH is always SHA256 + * + * @param secret Input secret + * @param secret_len length of the input secret + * @param label Label for the exported key material + * @param label_len length of the label + * @param context optional context + * @param context_len length of the context + * @param out output keying material + * @param out_len length of output keying material + * @return + */ +bool +ovpn_expand_label(const uint8_t *secret, size_t secret_len, + const uint8_t *label, size_t label_len, + const uint8_t *context, size_t context_len, + uint8_t *out, uint16_t out_len); + +#endif diff --git a/src/openvpn/crypto_mbedtls.h b/src/openvpn/crypto_mbedtls.h index a966a7a..fe51359 100644 --- a/src/openvpn/crypto_mbedtls.h +++ b/src/openvpn/crypto_mbedtls.h @@ -29,6 +29,7 @@ #ifndef CRYPTO_MBEDTLS_H_ #define CRYPTO_MBEDTLS_H_ +#include #include #include #include diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index a4e6235..307f9ed 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -66,6 +66,7 @@ $(top_srcdir)/src/openvpn/crypto.c \ $(top_srcdir)/src/openvpn/crypto_mbedtls.c \ $(top_srcdir)/src/openvpn/crypto_openssl.c \ + $(top_srcdir)/src/openvpn/crypto_epoch.c \ $(top_srcdir)/src/openvpn/otime.c \ $(top_srcdir)/src/openvpn/packet_id.c \ $(top_srcdir)/src/openvpn/platform.c \ diff --git a/tests/unit_tests/openvpn/test_crypto.c b/tests/unit_tests/openvpn/test_crypto.c index ec8e661..e16296b 100644 --- a/tests/unit_tests/openvpn/test_crypto.c +++ b/tests/unit_tests/openvpn/test_crypto.c @@ -35,12 +35,19 @@ #include #include "crypto.h" +#include "crypto_epoch.h" #include "options.h" #include "ssl_backend.h" #include "mss.h" #include "test_common.h" + +#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#endif + static const char testtext[] = "Dummy text to test PEM encoding"; static void @@ -471,6 +478,236 @@ assert_int_equal(aeslimit / L, 122059461); } +void +crypto_test_hkdf_expand_testa1(void **state) +{ + /* RFC 5889 A.1 Test Case 1 */ + uint8_t prk[32] = + {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}; + + uint8_t info[10] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9}; + + uint8_t okm[42] = + {0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65}; + + uint8_t out[42]; + ovpn_hkdf_expand(prk, info, sizeof(info), out, sizeof(out)); + + assert_memory_equal(out, okm, sizeof(out)); +} + +void +crypto_test_hkdf_expand_testa2(void **state) +{ + /* RFC 5889 A.2 Test Case 2 */ + uint8_t prk[32] = + {0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, + 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, + 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, + 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44}; + + uint8_t info[80] = + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; + + const int L = 82; + uint8_t okm[82] = + {0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, + 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, + 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, + 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, + 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, + 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, + 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, + 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87}; + + uint8_t out[82] = {0xaa}; + ovpn_hkdf_expand(prk, info, sizeof(info), out, L); + + assert_memory_equal(out, okm, L); +} + +void +crypto_test_hkdf_expand_testa3(void **state) +{ + /* RFC 5889 A.3 Test Case 3 */ + uint8_t prk[32] = + {0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, + 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, + 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77, + 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04}; + + uint8_t info[] = {}; + + int L = 42; + uint8_t okm[42] = + {0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, + 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, + 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, + 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, + 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, + 0x96, 0xc8}; + + uint8_t out[42]; + ovpn_hkdf_expand(prk, info, 0, out, L); + + assert_memory_equal(out, okm, L); +} + +void +crypto_test_hkdf_expand_test_ovpn(void **state) +{ + /* tests the HDKF with a label/okm that OpenVPN itself uses in OpenSSL 3 + * HDKF unit test*/ + + uint8_t prk[32] = + {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}; + + uint8_t info[18] = + {0x00, 0x1b, 0x0e, 0x6f, 0x76, 0x70, 0x6e, 0x20, + 0x75, 0x6e, 0x69, 0x74, 0x20, 0x74, 0x65, 0x73, + 0x74, 0x00}; + + int L = 27; + uint8_t okm[27] = + {0x87, 0x5a, 0x8e, 0xec, 0x18, 0x55, 0x63, 0x80, + 0xb8, 0xd9, 0x33, 0xed, 0x32, 0x3c, 0x2d, 0xf8, + 0xe8, 0xec, 0xcf, 0x49, 0x72, 0xe6, 0x83, 0xf0, + 0x6a, 0x83, 0xac }; + + uint8_t out[27]; + ovpn_hkdf_expand(prk, info, sizeof(info), out, L); + + assert_memory_equal(out, okm, L); +} + +void +crypto_test_ovpn_label_expand(void **state) +{ + uint8_t secret[32] = + {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}; + + const uint8_t *label = (const uint8_t *)("unit test"); + uint8_t out[16]; + ovpn_expand_label(secret, sizeof(secret), label, 9, NULL, 0, out, sizeof(out)); + + uint8_t out_expected[16] = + {0x18, 0x5e, 0xaa, 0x1c, 0x7f, 0x22, 0x8a, 0xb8, + 0xeb, 0x29, 0x77, 0x32, 0x14, 0xd9, 0x20, 0x46}; + + assert_memory_equal(out, out_expected, 16); +} + +#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L +/* We have OpenSSL 3.0+, we test if their implementation matches our + * implementation. We currently do not use this code from the crypto library + * in the main code yet as we don't want to repeat the mess that the current + * openvpn_PRF ifdef maze */ + +bool +ossl_expand_label(const uint8_t *secret, size_t secret_len, + const uint8_t *label, size_t label_len, + const uint8_t *context, size_t context_len, + uint8_t *out, uint16_t out_len) +{ + OSSL_LIB_CTX *libctx = NULL; + const char *properties = NULL; + + const uint8_t *label_prefix = (const uint8_t *) ("ovpn "); + const size_t label_prefix_len = 5; + + EVP_KDF *kdf = EVP_KDF_fetch(libctx, OSSL_KDF_NAME_TLS1_3_KDF, properties); + assert_non_null(kdf); + + const char *mdname = "SHA-256"; + + size_t hashlen = SHA256_DIGEST_LENGTH; + + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + assert_non_null(kctx); + + OSSL_PARAM params[7]; + OSSL_PARAM *p = params; + + int mode = EVP_PKEY_HKDEF_MODE_EXPAND_ONLY; + + *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *) mdname, 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, + (unsigned char *) secret, hashlen); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PREFIX, + (unsigned char *) label_prefix, + label_prefix_len); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_LABEL, + (unsigned char *) label, label_len); + + *p++ = OSSL_PARAM_construct_end(); + + int ret = EVP_KDF_derive(kctx, out, out_len, params); + EVP_KDF_CTX_free(kctx); + EVP_KDF_free(kdf); + + assert_int_equal(ret, 1); + return true; +} + +void +crypto_test_ovpn_expand_openssl3(void **state) +{ + uint8_t secret[32] = + {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}; + + const uint8_t *label = (const uint8_t *) ("unit test"); + const size_t labellen = 9; + uint8_t out[27]; + + ossl_expand_label(secret, sizeof(secret), label, labellen, NULL, 0, out, sizeof(out)); + + /* Do the same derivation with our own function */ + uint8_t out_ovpn[27]; + + ovpn_expand_label(secret, sizeof(secret), label, 9, NULL, 0, out_ovpn, sizeof(out_ovpn)); + assert_memory_equal(out_ovpn, out, sizeof(out_ovpn)); +} + +#else /* if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ +void +crypto_test_ovpn_expand_openssl3(void **state) +{ + skip(); +} +#endif /* if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + int main(void) { @@ -482,7 +719,13 @@ cmocka_unit_test(crypto_test_hmac), cmocka_unit_test(test_occ_mtu_calculation), cmocka_unit_test(test_mssfix_mtu_calculation), - cmocka_unit_test(crypto_test_aead_limits) + cmocka_unit_test(crypto_test_aead_limits), + cmocka_unit_test(crypto_test_hkdf_expand_testa1), + cmocka_unit_test(crypto_test_hkdf_expand_testa2), + cmocka_unit_test(crypto_test_hkdf_expand_testa3), + cmocka_unit_test(crypto_test_hkdf_expand_test_ovpn), + cmocka_unit_test(crypto_test_ovpn_label_expand), + cmocka_unit_test(crypto_test_ovpn_expand_openssl3) }; #if defined(ENABLE_CRYPTO_OPENSSL)