From patchwork Thu Jan 20 05:16:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 2237 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director12.mail.ord1d.rsapps.net ([172.27.255.58]) by backend41.mail.ord1d.rsapps.net with LMTP id CJCnGjuL6WENXAAAqwncew (envelope-from ) for ; Thu, 20 Jan 2022 11:18:03 -0500 Received: from proxy3.mail.iad3a.rsapps.net ([172.27.255.58]) by director12.mail.ord1d.rsapps.net with LMTP id kFNmLjuL6WHmRAAAIasKDg (envelope-from ) for ; Thu, 20 Jan 2022 11:18:03 -0500 Received: from smtp30.gate.iad3a ([172.27.255.58]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy3.mail.iad3a.rsapps.net with LMTPS id 6IwCKTuL6WGDQwAAYaqY3Q (envelope-from ) for ; Thu, 20 Jan 2022 11:18:03 -0500 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp30.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=gmail.com; dmarc=fail (p=none; dis=none) header.from=gmail.com X-Suspicious-Flag: YES X-Classification-ID: 8a973566-7a0c-11ec-9c8e-525400089674-1-1 Received: from [216.105.38.7] ([216.105.38.7:40706] helo=lists.sourceforge.net) by smtp30.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 25/7B-31019-B3B89E16; Thu, 20 Jan 2022 11:18:03 -0500 Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.94.2) (envelope-from ) id 1nAa7T-0006WQ-Bl; Thu, 20 Jan 2022 16:17:02 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1nAa7R-0006WB-EX for openvpn-devel@lists.sourceforge.net; Thu, 20 Jan 2022 16:17:00 +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:Cc:To:From:Sender:Reply-To: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=zxzoWmXADiB0inM+6aP9oxk8+jPM4NwogVxxsWN6qOQ=; b=LxJqqLdYLTYauve9pLkwwE6C29 D7IyHTWmPfYAqsvqSaDUteZQHSU2qFWyUQI2nHDeCtbDWgzsgtrb0VtuD+Fafm5lkjhU065/ZvPrl 0cCGStvPViJHnLisV7OnLtAac/NhfZBRtyYtFGsUL+sRegSmSW0pPht9K2wd9pcjH0Rk=; 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:Cc:To:From:Sender:Reply-To: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=zxzoWmXADiB0inM+6aP9oxk8+jPM4NwogVxxsWN6qOQ=; b=dNpVcd8ZYY+kIQAcVr+0Wk0NXu MO+e0RSVeuey0P6ayZPHhU1ikEXMQ+GCwbXqEGvseMycYrJ5lfaZd7Lq5xt+mHS4G7ES3m3jBLDzB F2lz61onC5neDdegc6aX0aFkylgKsVlvafemVHXSnv43wgGGdbhQehii7InCZMPZHeQI=; Received: from mail-qv1-f51.google.com ([209.85.219.51]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.92.3) id 1nAa7M-0007hK-AL for openvpn-devel@lists.sourceforge.net; Thu, 20 Jan 2022 16:17:00 +0000 Received: by mail-qv1-f51.google.com with SMTP id k9so6927095qvv.9 for ; Thu, 20 Jan 2022 08:16:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=zxzoWmXADiB0inM+6aP9oxk8+jPM4NwogVxxsWN6qOQ=; b=YA0Eu+I83w2OciCDCPad0bvLgt1zG8ylbg8a9Fm2KSeGy9n0glpQGyo6kpS3ZWgZkR JpzGcig1Z+uvxG5VeLWWJ3q6KLUdxNX07ErWuOxnPPKxpz5c0GrbQDtheFUKqe9d0QjS HIHMccH4/QEUDDz1adTVpc5reBp3j2QsHDMZQ4nbpqobdlwKQu8DNkUosou8CgGpumki s1aJasNZLXAmNo0au6Qt1vbfbuxrmzvilkujN1SDajgh9YbNH5iDBy5DxUDEDmB6L8S4 HdzmRw82wGWU5IHCmGuDp3Kx+cbRo3YlmerMJjmLnYpuNPPfW6vKLdTfuGr0LrwJc1Of NvUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=zxzoWmXADiB0inM+6aP9oxk8+jPM4NwogVxxsWN6qOQ=; b=Rrf9jUzeouoGV+/7HTeLues6vpNKffQXHMPAFx9nxcMWNbYy5hRBV11pAj083etXK8 6XDUEX49ETyMCOzm9J0M0qkOcYuD5fODX3qoKfKl8/gFRW063lCkShXN+M9IS6wgiBl9 g2YznOQSZ7YctMVMCRHjxrET7AU9vjOe8AncRSOSS/uMsXGIoM6svPeuRZDFH9fZwjT1 hNmaZhqLsWxMUPfq3KdA9mv5kZJUREEOqowQP0NoGtMnN5PTnaUzPXR+ChlivIfkQdbg N4IREcNBOg2NBFqrUi14bBPix6/3eYrbMAVp+sqa5oS6TIqgjxrv3S8MfRoKivh9CzCs O8Ig== X-Gm-Message-State: AOAM5309whocJ1VLXfvYR+vS3VOAxyNz2t4Iv7h/3AmV7wrbOGja85Ug taDHCX4pJl8qTXRMguuj2ihiHVWfrII= X-Google-Smtp-Source: ABdhPJzY2CEmGCIinLxl9zu1IqivSbUxG/E0jTOHV5dcCWXS3mE7PMoF6LNk8AUNOYklvSUhdC0BEQ== X-Received: by 2002:a05:6214:c69:: with SMTP id t9mr31565641qvj.100.1642695409972; Thu, 20 Jan 2022 08:16:49 -0800 (PST) Received: from uranus.home.sansel.ca (bras-vprn-tnhlon4053w-lp130-02-70-51-223-65.dsl.bell.ca. [70.51.223.65]) by smtp.gmail.com with ESMTPSA id z3sm1519878qtw.64.2022.01.20.08.16.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Jan 2022 08:16:49 -0800 (PST) From: selva.nair@gmail.com To: openvpn-devel@lists.sourceforge.net Date: Thu, 20 Jan 2022 11:16:16 -0500 Message-Id: <20220120161616.13447-1-selva.nair@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20211214165928.30676-18-selva.nair@gmail.com> References: <20211214165928.30676-18-selva.nair@gmail.com> MIME-Version: 1.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: Selva Nair Tests: - Check SIGNATURE and KEYMGMT methods can be fetched from the provider - Load sample RSA and EC keys as management-external-key and check that their sign callbacks are correctly exercised: with [...] Content analysis details: (-0.2 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [209.85.219.51 listed in list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [selva.nair[at]gmail.com] -0.0 RCVD_IN_MSPIKE_H2 RBL: Average reputation (+2) [209.85.219.51 listed in wl.mailspike.net] -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1nAa7M-0007hK-AL Subject: [Openvpn-devel] [PATCH v4 16+17/18] Add a unit test for external key provider 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 From: Selva Nair Tests: - Check SIGNATURE and KEYMGMT methods can be fetched from the provider - Load sample RSA and EC keys as management-external-key and check that their sign callbacks are correctly exercised: with and without digest support mocked in the client capability flag. -Test generic key load and signature v4: 16/18 and 17/18 of v3 squashed into one patch Signed-off-by: Selva Nair --- tests/unit_tests/openvpn/Makefile.am | 16 + tests/unit_tests/openvpn/test_provider.c | 403 +++++++++++++++++++++++ 2 files changed, 419 insertions(+) create mode 100644 tests/unit_tests/openvpn/test_provider.c diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index 44b77cc5..6b5c94ab 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -11,6 +11,8 @@ if HAVE_LD_WRAP_SUPPORT test_binaries += tls_crypt_testdriver endif +test_binaries += provider_testdriver + TESTS = $(test_binaries) check_PROGRAMS = $(test_binaries) @@ -95,6 +97,20 @@ networking_testdriver_SOURCES = test_networking.c mock_msg.c \ $(openvpn_srcdir)/platform.c endif +provider_testdriver_CFLAGS = @TEST_CFLAGS@ \ + -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ + $(OPTIONAL_CRYPTO_CFLAGS) +provider_testdriver_LDFLAGS = @TEST_LDFLAGS@ \ + $(OPTIONAL_CRYPTO_LIBS) + +provider_testdriver_SOURCES = test_provider.c mock_msg.c \ + $(openvpn_srcdir)/xkey_helper.c \ + $(openvpn_srcdir)/xkey_provider.c \ + $(openvpn_srcdir)/buffer.c \ + $(openvpn_srcdir)/base64.c \ + mock_get_random.c \ + $(openvpn_srcdir)/platform.c + auth_token_testdriver_CFLAGS = @TEST_CFLAGS@ \ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \ $(OPTIONAL_CRYPTO_CFLAGS) diff --git a/tests/unit_tests/openvpn/test_provider.c b/tests/unit_tests/openvpn/test_provider.c new file mode 100644 index 00000000..0182b3b4 --- /dev/null +++ b/tests/unit_tests/openvpn/test_provider.c @@ -0,0 +1,403 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2021 Selva Nair + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, + * or (at your option) any later version. + * + * 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" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "manage.h" +#include "xkey_common.h" + +#ifdef HAVE_XKEY_PROVIDER + +#include +#include +#include +#include +#include +#include + +struct management *management; /* global */ +static int mgmt_callback_called; + +#ifndef _countof +#define _countof(x) sizeof((x))/sizeof(*(x)) +#endif + +static OSSL_PROVIDER *prov[2]; + +/* public keys for testing -- RSA and EC */ +static const char * const pubkey1 = "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7GWP6RLCGlvmVioIqYI6\n" + "LUR4owA7sJ/nJxBAk+/xzD6gqgSigBsTqeb+gdZwkKjY1N4w2DUA0r5i8Eja/BWN\n" + "xMZtC5nxK4MACtMqIwvlzfk130NhFXKtlZj2cyFBXqDdRyeg1ZrUQagcHVcgcReP\n" + "9yiePgfO7NUOQk8edEeOR53SFCgnLBQQ9dGWtZN0hO/5BN6NSm/fd6vq0VjTRP5a\n" + "BAH/BnqX9/3jV0jh8N9AE59mI1rjVVQ9VDnuAPkS8dLfdC661/CNxt0YWByTIgt1\n" + "+qjW4LUvLbnU/rlPhuJ1SBZg+z/JtDBCKfs7syu5WYFqRvNFg7/91Rr/NwxvW/1h\n" + "8QIDAQAB\n" + "-----END PUBLIC KEY-----\n"; + +static const char * const pubkey2 = "-----BEGIN PUBLIC KEY-----\n" + "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEO85iXW+HgnUkwlj1DohNVw0GsnGIh1gZ\n" + "u95ff1JiUaJIkYNIkZA+hwIPFVH5aJcSCv3SPIeDS2VUAESNKHZJBQ==\n" + "-----END PUBLIC KEY-----\n"; + +static const char *pubkeys[] = {pubkey1, pubkey2}; + +static const char *prov_name = "ovpn.xkey"; + +static const char* test_msg = "Lorem ipsum dolor sit amet, consectetur " + "adipisici elit, sed eiusmod tempor incidunt " + "ut labore et dolore magna aliqua."; + +static const char* test_msg_b64 = + "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2ljaS" + "BlbGl0LCBzZWQgZWl1c21vZCB0ZW1wb3IgaW5jaWR1bnQgdXQgbGFib3JlIGV0IGRv" + "bG9yZSBtYWduYSBhbGlxdWEu"; + +/* Sha256 digest of test_msg excluding NUL terminator */ +static const uint8_t test_digest[] = + {0x77, 0x38, 0x65, 0x00, 0x1e, 0x96, 0x48, 0xc6, 0x57, 0x0b, 0xae, + 0xc0, 0xb7, 0x96, 0xf9, 0x66, 0x4d, 0x5f, 0xd0, 0xb7, 0xdb, 0xf3, + 0x3a, 0xbf, 0x02, 0xcc, 0x78, 0x61, 0x83, 0x20, 0x20, 0xee}; + +static const char *test_digest_b64 = "dzhlAB6WSMZXC67At5b5Zk1f0Lfb8zq/Asx4YYMgIO4="; + +/* Dummy signature used only to check that the expected callback + * was successfully exercised. Keep this shorter than 64 bytes + * --- the smallest size of the actual signature with the above + * keys. + */ +static const uint8_t good_sig[] = + {0xd8, 0xa7, 0xd9, 0x81, 0xd8, 0xaa, 0xd8, 0xad, 0x20, 0xd9, 0x8a, 0xd8, + 0xa7, 0x20, 0xd8, 0xb3, 0xd9, 0x85, 0xd8, 0xb3, 0xd9, 0x85, 0x0}; + +static const char *good_sig_b64 = "2KfZgdiq2K0g2YrYpyDYs9mF2LPZhQA="; + +static EVP_PKEY * +load_pubkey(const char *pem) +{ + BIO *in = BIO_new_mem_buf(pem, -1); + assert_non_null(in); + + EVP_PKEY *pkey = PEM_read_bio_PUBKEY(in, NULL, NULL, NULL); + assert_non_null(pkey); + + BIO_free(in); + return pkey; +} + +static void +init_test() +{ + prov[0] = OSSL_PROVIDER_load(NULL,"default"); + OSSL_PROVIDER_add_builtin(NULL, prov_name, xkey_provider_init); + prov[1] = OSSL_PROVIDER_load(NULL, prov_name); + + /* set default propq matching what we use in ssl_openssl.c */ + EVP_set_default_properties(NULL, "?provider!=ovpn.xkey"); + + management = test_calloc(sizeof(*management), 1); +} + +static void +uninit_test() +{ + for (size_t i = 0; i < _countof(prov); i++) + { + if (prov[i]) + { + OSSL_PROVIDER_unload(prov[i]); + } + } + test_free(management); +} + +/* Mock management callback for signature. + * We check that the received data to sign matches test_msg or + * test_digest and return a predefined string as signature so that + * the caller can validate all steps up to sending the data to + * the management client. + */ +char * +management_query_pk_sig(struct management *man, const char *b64_data, + const char *algorithm) +{ + char *out = NULL; + + /* indicate entry to the callback */ + mgmt_callback_called = 1; + + const char *expected_tbs = test_digest_b64; + if (strstr(algorithm, "data=message")) + { + expected_tbs = test_msg_b64; + assert_non_null(strstr(algorithm, "hashalg=SHA256")); + } + assert_string_equal(b64_data, expected_tbs); + + /* We test using ECDSA or PSS with saltlen = digest */ + if (!strstr(algorithm, "ECDSA")) + { + assert_non_null(strstr(algorithm, "RSA_PKCS1_PSS_PADDING,hashalg=SHA256,saltlen=digest")); + } + + /* Return a predefined string as sig so that the caller + * can confirm that this callback was exercised. + */ + out = strdup(good_sig_b64); + assert_non_null(out); + + return out; +} + +/* Check signature and keymgmt methods can be fetched from the provider */ +static void +xkey_provider_test_fetch(void **state) +{ + assert_true(OSSL_PROVIDER_available(NULL, prov_name)); + + const char *algs[] = {"RSA", "ECDSA"}; + + for (size_t i = 0; i < _countof(algs); i++) + { + EVP_SIGNATURE *sig = EVP_SIGNATURE_fetch(NULL, algs[i], "provider=ovpn.xkey"); + assert_non_null(sig); + assert_string_equal(OSSL_PROVIDER_get0_name(EVP_SIGNATURE_get0_provider(sig)), prov_name); + + EVP_SIGNATURE_free(sig); + } + + const char *names[] = {"RSA", "EC"}; + + for (size_t i = 0; i < _countof(names); i++) + { + EVP_KEYMGMT *km = EVP_KEYMGMT_fetch(NULL, names[i], "provider=ovpn.xkey"); + assert_non_null(km); + assert_string_equal(OSSL_PROVIDER_get0_name(EVP_KEYMGMT_get0_provider(km)), prov_name); + + EVP_KEYMGMT_free(km); + } +} + +/* sign a test message using pkey -- caller must free the returned sig */ +static uint8_t * +digest_sign(EVP_PKEY *pkey) +{ + uint8_t *sig = NULL; + size_t siglen = 0; + + OSSL_PARAM params[6] = {OSSL_PARAM_END}; + + const char *mdname = "SHA256"; + const char *padmode = "pss"; + const char *saltlen = "digest"; + + if (EVP_PKEY_get_id(pkey) == EVP_PKEY_RSA) + { + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, (char *)mdname, 0); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, (char *)padmode, 0); + params[2] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, (char *)saltlen, 0); + /* same digest for mgf1 */ + params[3] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, (char *)saltlen, 0); + params[4] = OSSL_PARAM_construct_end(); + } + + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + + if (!mctx + || EVP_DigestSignInit_ex(mctx, &pctx, mdname, NULL, NULL, pkey, params) <= 0) + { + fail_msg("Failed to initialize EVP_DigestSignInit_ex()"); + goto done; + } + + /* sign with sig = NULL to get required siglen */ + assert_int_equal(EVP_DigestSign(mctx, sig, &siglen, (uint8_t*)test_msg, strlen(test_msg)), 1); + assert_true(siglen > 0); + + if ((sig = test_calloc(1, siglen)) == NULL) + { + fail_msg("Out of memory"); + } + assert_int_equal(EVP_DigestSign(mctx, sig, &siglen, (uint8_t*)test_msg, strlen(test_msg)), 1); + +done: + if (mctx) + { + EVP_MD_CTX_free(mctx); /* pctx is internally allocated and freed by mctx */ + } + return sig; +} + +/* Check loading of management external key and have sign callback exercised + * for RSA and EC keys with and without digest support in management client. + * Sha256 digest used for both cases with pss padding for RSA. + */ +static void +xkey_provider_test_mgmt_sign_cb(void **state) +{ + EVP_PKEY *pubkey; + for (size_t i = 0; i < _countof(pubkeys); i++) + { + pubkey = load_pubkey(pubkeys[i]); + assert_true(pubkey != NULL); + EVP_PKEY *privkey = xkey_load_management_key(NULL, pubkey); + assert_true(privkey != NULL); + + management->settings.flags = MF_EXTERNAL_KEY|MF_EXTERNAL_KEY_PSSPAD; + + /* first without digest support in management client */ +again: + mgmt_callback_called = 0; + uint8_t *sig = digest_sign(privkey); + assert_non_null(sig); + + /* check callback for signature got exercised */ + assert_int_equal(mgmt_callback_called, 1); + assert_memory_equal(sig, good_sig, sizeof(good_sig)); + test_free(sig); + + if (!(management->settings.flags & MF_EXTERNAL_KEY_DIGEST)) + { + management->settings.flags |= MF_EXTERNAL_KEY_DIGEST; + goto again; /* this time with digest support announced */ + } + + EVP_PKEY_free(pubkey); + EVP_PKEY_free(privkey); + } +} + +/* helpers for testing generic key load and sign */ +static int xkey_free_called; +static int xkey_sign_called; +static void +xkey_free(void *handle) +{ + xkey_free_called = 1; + /* We use a dummy string as handle -- check its value */ + assert_string_equal(handle, "xkey_handle"); +} + +static int +xkey_sign(void *handle, unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen, XKEY_SIGALG s) +{ + if (!sig) + { + *siglen = 256; /* some arbitrary size */ + return 1; + } + + xkey_sign_called = 1; /* called with non-null sig */ + + if (!strcmp(s.op, "DigestSign")) + { + assert_memory_equal(tbs, test_msg, strlen(test_msg)); + } + else + { + assert_memory_equal(tbs, test_digest, sizeof(test_digest)); + } + + /* For the test use sha256 and PSS padding for RSA */ + assert_int_equal(OBJ_sn2nid(s.mdname), NID_sha256); + if (!strcmp(s.keytype, "RSA")) + { + assert_string_equal(s.padmode, "pss"); /* we use PSS for the test */ + } + else if (strcmp(s.keytype, "EC")) + { + fail_msg("Unknown keytype: %s", s.keytype); + } + + /* return a predefined string as sig */ + memcpy(sig, good_sig, min_int(sizeof(good_sig), *siglen)); + + return 1; +} + +/* Load a key as a generic key and check its sign op gets + * called for signature. + */ +static void +xkey_provider_test_generic_sign_cb(void **state) +{ + EVP_PKEY *pubkey; + const char *dummy = "xkey_handle"; /* a dummy handle for the external key */ + + for (size_t i = 0; i < _countof(pubkeys); i++) + { + pubkey = load_pubkey(pubkeys[i]); + assert_true(pubkey != NULL); + + EVP_PKEY *privkey = xkey_load_generic_key(NULL, (void*)dummy, pubkey, xkey_sign, xkey_free); + assert_true(privkey != NULL); + + xkey_sign_called = 0; + xkey_free_called = 0; + uint8_t *sig = digest_sign(privkey); + assert_non_null(sig); + + /* check callback for signature got exercised */ + assert_int_equal(xkey_sign_called, 1); + assert_memory_equal(sig, good_sig, sizeof(good_sig)); + test_free(sig); + + EVP_PKEY_free(pubkey); + EVP_PKEY_free(privkey); + + /* check key's free-op got called */ + assert_int_equal(xkey_free_called, 1); + } +} + +int +main(void) +{ + init_test(); + + const struct CMUnitTest tests[] = { + cmocka_unit_test(xkey_provider_test_fetch), + cmocka_unit_test(xkey_provider_test_mgmt_sign_cb), + cmocka_unit_test(xkey_provider_test_generic_sign_cb), + }; + + int ret = cmocka_run_group_tests_name("xkey provider tests", tests, NULL, NULL); + + uninit_test(); + return ret; +} +#else +int +main(void) +{ + return 0; +} +#endif /* HAVE_XKEY_PROVIDER */