mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-09-20 03:52:28 +02:00
Add a test for signing with certificates in Windows store
- For each sample certificate/key pair imported into the store, load the key into xkey-provider and sign a test message. As the key is "provided", signing will use appropriate backend (Windows CNG in this case). The signature is then verified using OpenSSL. Change-Id: I520b34ba51e8c6d0247a82edc52bde181ab5a717 Signed-off-by: Selva Nair <selva.nair@gmail.com> Acked-by: Gert Doering <gert@greenie.muc.de> Message-Id: <20230315013516.1256700-5-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg26416.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
parent
0ad5f4d6c4
commit
0267649a21
@ -157,6 +157,7 @@ cryptoapi_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
|
||||
$(OPTIONAL_CRYPTO_LIBS) -lcrypt32 -lncrypt
|
||||
cryptoapi_testdriver_SOURCES = test_cryptoapi.c mock_msg.c \
|
||||
$(top_srcdir)/src/openvpn/xkey_helper.c \
|
||||
$(top_srcdir)/src/openvpn/xkey_provider.c \
|
||||
$(top_srcdir)/src/openvpn/buffer.c \
|
||||
$(top_srcdir)/src/openvpn/base64.c \
|
||||
$(top_srcdir)/src/openvpn/platform.c \
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <cryptoapi.c> /* pull-in the whole file to test static functions */
|
||||
|
||||
struct management *management; /* global */
|
||||
static OSSL_PROVIDER *prov[2];
|
||||
|
||||
/* mock a management function that xkey_provider needs */
|
||||
char *
|
||||
@ -66,6 +67,11 @@ OSSL_LIB_CTX *tls_libctx;
|
||||
#define _countof(x) sizeof((x))/sizeof(*(x))
|
||||
#endif
|
||||
|
||||
/* A message for signing */
|
||||
static const char *test_msg = "Lorem ipsum dolor sit amet, consectetur "
|
||||
"adipisici elit, sed eiusmod tempor incidunt "
|
||||
"ut labore et dolore magna aliqua.";
|
||||
|
||||
/* test data */
|
||||
static const uint8_t test_hash[] = {
|
||||
0x77, 0x38, 0x65, 0x00, 0x1e, 0x96, 0x48, 0xc6, 0x57, 0x0b, 0xae,
|
||||
@ -336,6 +342,164 @@ test_find_cert_byissuer(void **state)
|
||||
gc_free(&gc);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_cryptoapi_sign(void **state)
|
||||
{
|
||||
(void) state;
|
||||
/* Initialize providers in a way matching what OpenVPN core does */
|
||||
tls_libctx = OSSL_LIB_CTX_new();
|
||||
prov[0] = OSSL_PROVIDER_load(tls_libctx, "default");
|
||||
OSSL_PROVIDER_add_builtin(tls_libctx, "ovpn.xkey", xkey_provider_init);
|
||||
prov[1] = OSSL_PROVIDER_load(tls_libctx, "ovpn.xkey");
|
||||
|
||||
/* set default propq as we do in ssl_openssl.c */
|
||||
EVP_set_default_properties(tls_libctx, "?provider!=ovpn.xkey");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
teardown_cryptoapi_sign(void **state)
|
||||
{
|
||||
(void) state;
|
||||
for (size_t i = 0; i < _countof(prov); i++)
|
||||
{
|
||||
if (prov[i])
|
||||
{
|
||||
OSSL_PROVIDER_unload(prov[i]);
|
||||
prov[i] = NULL;
|
||||
}
|
||||
}
|
||||
OSSL_LIB_CTX_free(tls_libctx);
|
||||
tls_libctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign "test_msg" using a private key. The key may be a "provided" key
|
||||
* in which case its signed by the provider's backend -- cryptoapi in our
|
||||
* case. Then verify the signature using OpenSSL.
|
||||
* Returns 1 on success, 0 on error.
|
||||
*/
|
||||
static int
|
||||
digest_sign_verify(EVP_PKEY *privkey, EVP_PKEY *pubkey)
|
||||
{
|
||||
uint8_t *sig = NULL;
|
||||
size_t siglen = 0;
|
||||
int ret = 0;
|
||||
|
||||
OSSL_PARAM params[2] = {OSSL_PARAM_END};
|
||||
const char *mdname = "SHA256";
|
||||
|
||||
if (EVP_PKEY_get_id(privkey) == EVP_PKEY_RSA)
|
||||
{
|
||||
const char *padmode = "pss"; /* RSA_PSS: for all other params, use defaults */
|
||||
params[0] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE,
|
||||
(char *)padmode, 0);
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
}
|
||||
else if (EVP_PKEY_get_id(privkey) == EVP_PKEY_EC)
|
||||
{
|
||||
params[0] = OSSL_PARAM_construct_end();
|
||||
}
|
||||
else
|
||||
{
|
||||
print_error("Unknown key type in digest_sign_verify()");
|
||||
return ret;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_MD_CTX *mctx = EVP_MD_CTX_new();
|
||||
|
||||
if (!mctx
|
||||
|| EVP_DigestSignInit_ex(mctx, &pctx, mdname, tls_libctx, NULL, privkey, params) <= 0)
|
||||
{
|
||||
/* cmocka assert output for these kinds of failures is hardly explanatory,
|
||||
* print a message and assert in caller. */
|
||||
print_error("Failed to initialize EVP_DigestSignInit_ex()\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* sign with sig = NULL to get required siglen */
|
||||
if (EVP_DigestSign(mctx, sig, &siglen, (uint8_t *)test_msg, strlen(test_msg)) != 1)
|
||||
{
|
||||
print_error("EVP_DigestSign: failed to get required signature size");
|
||||
goto done;
|
||||
}
|
||||
assert_true(siglen > 0);
|
||||
|
||||
if ((sig = test_calloc(1, siglen)) == NULL)
|
||||
{
|
||||
print_error("Out of memory");
|
||||
goto done;
|
||||
}
|
||||
if (EVP_DigestSign(mctx, sig, &siglen, (uint8_t *)test_msg, strlen(test_msg)) != 1)
|
||||
{
|
||||
print_error("EVP_DigestSign: signing failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now validate the signature using OpenSSL. Just use the public key
|
||||
* which is a native OpenSSL key.
|
||||
*/
|
||||
EVP_MD_CTX_free(mctx); /* this also frees pctx */
|
||||
mctx = EVP_MD_CTX_new();
|
||||
pctx = NULL;
|
||||
if (!mctx
|
||||
|| EVP_DigestVerifyInit_ex(mctx, &pctx, mdname, tls_libctx, NULL, pubkey, params) <= 0)
|
||||
{
|
||||
print_error("Failed to initialize EVP_DigestVerifyInit_ex()");
|
||||
goto done;
|
||||
}
|
||||
if (EVP_DigestVerify(mctx, sig, siglen, (uint8_t *)test_msg, strlen(test_msg)) != 1)
|
||||
{
|
||||
print_error("EVP_DigestVerify failed");
|
||||
goto done;
|
||||
}
|
||||
ret = 1;
|
||||
|
||||
done:
|
||||
if (mctx)
|
||||
{
|
||||
EVP_MD_CTX_free(mctx); /* this also frees pctx */
|
||||
}
|
||||
test_free(sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Load sample certificates & keys, sign a test message using
|
||||
* them and verify the signature.
|
||||
*/
|
||||
void
|
||||
test_cryptoapi_sign(void **state)
|
||||
{
|
||||
(void) state;
|
||||
char select_string[64];
|
||||
X509 *x509 = NULL;
|
||||
EVP_PKEY *privkey = NULL;
|
||||
|
||||
import_certs(state); /* a no-op if already imported */
|
||||
assert_true(certs_loaded);
|
||||
|
||||
for (struct test_cert *c = certs; c->cert; c++)
|
||||
{
|
||||
if (c->valid == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
openvpn_snprintf(select_string, sizeof(select_string), "THUMB:%s", c->hash);
|
||||
if (Load_CryptoAPI_certificate(select_string, &x509, &privkey) != 1)
|
||||
{
|
||||
fail_msg("Load_CryptoAPI_certificate failed: <%s>", c->friendly_name);
|
||||
return;
|
||||
}
|
||||
EVP_PKEY *pubkey = X509_get_pubkey(x509);
|
||||
assert_int_equal(digest_sign_verify(privkey, pubkey), 1);
|
||||
X509_free(x509);
|
||||
EVP_PKEY_free(privkey);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_hexstring(void **state)
|
||||
{
|
||||
@ -366,6 +530,8 @@ main(void)
|
||||
cmocka_unit_test(test_find_cert_bythumb),
|
||||
cmocka_unit_test(test_find_cert_byname),
|
||||
cmocka_unit_test(test_find_cert_byissuer),
|
||||
cmocka_unit_test_setup_teardown(test_cryptoapi_sign, setup_cryptoapi_sign,
|
||||
teardown_cryptoapi_sign),
|
||||
};
|
||||
|
||||
int ret = cmocka_run_group_tests_name("cryptoapi tests", tests, NULL, cleanup);
|
||||
|
Loading…
Reference in New Issue
Block a user