diff --git a/.gitignore b/.gitignore index d224b76e..638da86e 100644 --- a/.gitignore +++ b/.gitignore @@ -128,5 +128,6 @@ src/tests/lottery src/tests/p15dump src/tests/pintest src/tests/prngtest +src/tests/p11test/p11test version.m4.ci diff --git a/.travis.yml b/.travis.yml index 66dbd557..8974368f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: c -sudo: false - addons: apt_packages: - binutils-mingw-w64-i686 @@ -15,6 +13,7 @@ addons: - xsltproc - gengetopt - help2man + - libcmocka-dev env: global: @@ -52,7 +51,7 @@ before_install: brew update; brew uninstall libtool; brew install libtool; - brew install gengetopt help2man; + brew install gengetopt help2man cmocka; fi before_script: diff --git a/configure.ac b/configure.ac index 46e90e0d..98fbec56 100644 --- a/configure.ac +++ b/configure.ac @@ -255,6 +255,13 @@ AC_ARG_ENABLE( [enable_notify="detect"] ) +AC_ARG_ENABLE( + [tests], + [AS_HELP_STRING([--enable-tests],[Build tests in src/tests/ directory @<:@detect@:>@])], + , + [enable_tests="detect"] +) + AC_ARG_WITH( [xsl-stylesheetsdir], [AS_HELP_STRING([--with-xsl-stylesheetsdir=PATH],[docbook xsl-stylesheets for svn build @<:@detect@:>@])], @@ -511,6 +518,26 @@ if test "${enable_notify}" = "yes"; then fi fi +PKG_CHECK_MODULES( [CMOCKA], [cmocka], + [ have_cmocka="yes" ], + [ have_cmocka="no" ]) + +if test "${enable_tests}" = "detect"; then + if test "${have_cmocka}" = "yes"; then + enable_tests="yes" + else + enable_tests="no" + fi +fi + +if test "${enable_tests}" = "yes"; then + if test "${have_cmocka}" = "yes"; then + AC_DEFINE([ENABLE_TESTS], [1], [Build tests in tests/ subdirectory]) + else + AC_MSG_ERROR([Tests required, but cmocka is not available]) + fi +fi + AC_ARG_VAR([ZLIB_CFLAGS], [C compiler flags for zlib]) AC_ARG_VAR([ZLIB_LIBS], [linker flags for zlib]) if test -z "${ZLIB_LIBS}"; then @@ -997,6 +1024,7 @@ AM_CONDITIONAL([ENABLE_MINIDRIVER_SETUP_CUSTOMACTION], [test "${enable_minidrive AM_CONDITIONAL([ENABLE_SM], [test "${enable_sm}" = "yes"]) AM_CONDITIONAL([ENABLE_DNIE_UI], [test "${enable_dnie_ui}" = "yes"]) AM_CONDITIONAL([ENABLE_NPATOOL], [test "${ENABLE_NPATOOL}" = "yes"]) +AM_CONDITIONAL([ENABLE_TESTS], [test "${ENABLE_TESTS}" = "yes"]) AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"]) if test "${enable_pedantic}" = "yes"; then @@ -1025,6 +1053,7 @@ AC_CONFIG_FILES([ src/scconf/Makefile src/tests/Makefile src/tests/regression/Makefile + src/tests/p11test/Makefile src/tools/Makefile src/tools/versioninfo-tools.rc src/tools/versioninfo-opensc-notify.rc @@ -1082,6 +1111,7 @@ SM support: ${enable_sm} SM default module: ${DEFAULT_SM_MODULE} DNIe UI support: ${enable_dnie_ui} Notification support: ${enable_notify} +Build tests: ${enable_tests} Debug file: ${DEBUG_FILE} PC/SC default provider: ${DEFAULT_PCSC_PROVIDER} diff --git a/src/Makefile.am b/src/Makefile.am index 64d05d93..3be8f6a9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,8 +3,12 @@ EXTRA_DIST = Makefile.mak # Order IS important SUBDIRS = common scconf ui pkcs15init sm \ - libopensc pkcs11 tools tests minidriver + libopensc pkcs11 tools minidriver if ENABLE_SM SUBDIRS += smm endif + +if ENABLE_TESTS +SUBDIRS += tests +endif diff --git a/src/Makefile.mak b/src/Makefile.mak index f6609f77..c3779216 100644 --- a/src/Makefile.mak +++ b/src/Makefile.mak @@ -1,7 +1,7 @@ TOPDIR = .. SUBDIRS = common scconf ui sm pkcs15init \ - libopensc pkcs11 tools tests + libopensc pkcs11 tools default: all @@ -15,6 +15,10 @@ SUBDIRS = $(SUBDIRS) minidriver SUBDIRS = $(SUBDIRS) smm !ENDIF +!IF "$(TESTS_DEF)" == "/DENABLE_TESTS" +SUBDIRS = $(SUBDIRS) tests +!ENDIF + all clean:: @for %i in ( $(SUBDIRS) ) do \ @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" diff --git a/src/libopensc/sc-ossl-compat.h b/src/libopensc/sc-ossl-compat.h index a94d9564..9801cf52 100644 --- a/src/libopensc/sc-ossl-compat.h +++ b/src/libopensc/sc-ossl-compat.h @@ -93,6 +93,7 @@ extern "C" { #define OPENSSL_malloc_init CRYPTO_malloc_init #define EVP_PKEY_get0_RSA(x) (x->pkey.rsa) +#define EVP_PKEY_get0_EC_KEY(x) (x->pkey.ec) #define EVP_PKEY_get0_DSA(x) (x->pkey.dsa) #define X509_get_extension_flags(x) (x->ex_flags) #define X509_get_key_usage(x) (x->ex_kusage) @@ -103,6 +104,11 @@ extern "C" { #endif #endif +/* ASN1_STRING_data is deprecated in OpenSSL 1.1.0 */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) +#endif + /* * OpenSSL-1.1.0-pre5 has hidden the RSA and DSA structures * One can no longer use statements like rsa->n = ... @@ -140,6 +146,9 @@ extern "C" { #ifndef OPENSSL_NO_DSA #include #endif +#ifndef OPENSSL_NO_EC +#include +#endif #ifndef OPENSSL_NO_RSA static sc_ossl_inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) @@ -239,6 +248,21 @@ static sc_ossl_inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, co /* NOTE: DSA_set0_* functions not defined because they are not currently used in OpenSC */ #endif /* OPENSSL_NO_DSA */ + +#ifndef OPENSSL_NO_EC +static sc_ossl_inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} +#endif /* OPENSSL_NO_EC */ + + #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ #ifdef __cplusplus diff --git a/src/pkcs11/pkcs11.h b/src/pkcs11/pkcs11.h index 692f5e36..61a5050d 100644 --- a/src/pkcs11/pkcs11.h +++ b/src/pkcs11/pkcs11.h @@ -776,7 +776,7 @@ typedef struct CK_RSA_PKCS_OAEP_PARAMS { typedef struct CK_RSA_PKCS_PSS_PARAMS { ck_mechanism_type_t hashAlg; - unsigned long mgf; + CK_RSA_PKCS_MGF_TYPE mgf; unsigned long sLen; } CK_RSA_PKCS_PSS_PARAMS; @@ -786,6 +786,8 @@ typedef struct CK_RSA_PKCS_PSS_PARAMS { #define CKG_MGF1_SHA384 (0x00000003UL) #define CKG_MGF1_SHA512 (0x00000004UL) +#define CKZ_DATA_SPECIFIED (0x00000001UL) + typedef unsigned long ck_rv_t; diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 2a869d89..99ee8aa6 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -3,7 +3,7 @@ include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak -SUBDIRS = regression +SUBDIRS = regression p11test noinst_PROGRAMS = base64 lottery p15dump pintest prngtest AM_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/tests/p11test/Makefile.am b/src/tests/p11test/Makefile.am new file mode 100644 index 00000000..15de3174 --- /dev/null +++ b/src/tests/p11test/Makefile.am @@ -0,0 +1,30 @@ +include $(top_srcdir)/win32/ltrc.inc + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak + +noinst_PROGRAMS = p11test +noinst_HEADERS = p11test_loader.h p11test_case_common.h \ + p11test_case_readonly.h p11test_case_multipart.h \ + p11test_case_mechs.h p11test_case_ec_sign.h \ + p11test_case_usage.h p11test_case_wait.h \ + p11test_case_pss_oaep.h p11test_helpers.h + +AM_CPPFLAGS = -I$(top_srcdir)/src + +p11test_SOURCES = p11test.c p11test_loader.c \ + p11test_case_common.c \ + p11test_case_readonly.c \ + p11test_case_multipart.c \ + p11test_case_mechs.c \ + p11test_case_ec_sign.c \ + p11test_case_usage.c \ + p11test_case_wait.c \ + p11test_case_pss_oaep.c \ + p11test_helpers.c +p11test_CFLAGS = -DNDEBUG +p11test_LDADD = -lssl -lcrypto -lcmocka + +if WIN32 +p11test_SOURCES += $(top_builddir)/win32/versioninfo.rc +endif diff --git a/src/tests/p11test/Makefile.mak b/src/tests/p11test/Makefile.mak new file mode 100644 index 00000000..19783013 --- /dev/null +++ b/src/tests/p11test/Makefile.mak @@ -0,0 +1,26 @@ +TOPDIR = ..\..\.. + +TARGETS = p11test.exe + +OBJECTS = p11test_loader.obj \ + p11test_case_common.obj \ + p11test_case_readonly.obj \ + p11test_case_multipart.obj \ + p11test_case_mechs.obj \ + p11test_case_ec_sign.obj \ + p11test_case_usage.obj \ + p11test_case_wait.obj \ + p11test_case_pss_oaep.obj \ + p11test_helpers.obj \ + $(TOPDIR)\win32\versioninfo.res + +all: $(TARGETS) + +!INCLUDE $(TOPDIR)\win32\Make.rules.mak + +$(TARGETS): $(OBJECTS) $(LIBS) + +.c.exe: + cl $(COPTS) /c $< + link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) + if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 diff --git a/src/tests/p11test/README.md b/src/tests/p11test/README.md new file mode 100644 index 00000000..bcf9a58d --- /dev/null +++ b/src/tests/p11test/README.md @@ -0,0 +1,57 @@ +# Non-destructive PKCS#11 test suite (not only for readonly cards) + +## What are the dependencies? + +In addition to the dependencies needed by OpenSC, the test suite is +using [`cmocka`](https://cmocka.org/) unit testing framework +(`libcmocka-devel` package in Fedora/EPEL). + +## How to use? + +Build OpenSC from source: + + git clone git@github.com:Jakuje/OpenSC.git + cd OpenSC + git checkout jjelen-testsuite # not in master yet + ./bootstrap + ./configure + make -j4 + +Plug in the card/reader, change to test directory and run the test: + + cd src/tests/p11test + ./p11test -p 123456 + +It will run all tests on the first card found in PKCS#11 API +with pin `123456` and using just built OpenSC shared library from master. + +### I have more slots with different cards. + +Slot can be selected using `-s` switch on command-line. + + ./p11test -s 4 + +Slot numbers can be obtained using from `pkcs11-tool -L` (note that different +libraries might have different numbers for the slots). + +### I want to test different pkcs11 library + +You can specify different library or build from different branch +on command-line: + + ./p11test -m /usr/lib64/pkcs11/libcoolkeypk11.so + +or to debug PKCS#11 calls using `/usr/lib64/pkcs11-spy.so`: + + export PKCS11SPY="../pkcs11/.libs/opensc-pkcs11.so" + ./p11test -m ../pkcs11/.libs/pkcs11-spy.so + +You can run the test suite also on the soft tokens. The testbench for +`softhsm` and `opencryptoki` is available in the script `runtest.sh`. + +TODO: + + * Test `CKM_ECDSA_DERIVE` mechanism(s) + * Read pin from environment variable? + * Keygen write tests (optional) + * Reflect cmocka dependency in the configure diff --git a/src/tests/p11test/cert.cfg b/src/tests/p11test/cert.cfg new file mode 100644 index 00000000..409aa930 --- /dev/null +++ b/src/tests/p11test/cert.cfg @@ -0,0 +1,6 @@ +organization = "OpenSC" +expiration_days = 365 +email = "none@example.org" +signing_key +encryption_key + diff --git a/src/tests/p11test/p11test.c b/src/tests/p11test/p11test.c new file mode 100644 index 00000000..5e004c62 --- /dev/null +++ b/src/tests/p11test/p11test.c @@ -0,0 +1,132 @@ +/* + * p11test.c: Test suite for PKCS#11 API + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include +#include "p11test_helpers.h" + +#include "p11test_case_readonly.h" +#include "p11test_case_multipart.h" +#include "p11test_case_ec_sign.h" +#include "p11test_case_usage.h" +#include "p11test_case_mechs.h" +#include "p11test_case_wait.h" +#include "p11test_case_pss_oaep.h" + +#define DEFAULT_P11LIB "../../pkcs11/.libs/opensc-pkcs11.so" + +void display_usage() { + fprintf(stdout, + " Usage:\n" + " ./p11test [-m module_path] [-s slot_id] [-p pin]\n" + " -m module_path Path to tested module (e.g. /usr/lib64/opensc-pkcs11.so)\n" + " Default is "DEFAULT_P11LIB"\n" + " -p pin Application PIN\n" + " -s slot_id Slot ID with the card\n" + " -i Wait for the card before running the test (interactive)\n" + " -o File to write a log in JSON\n" + " -h This help\n" + "\n"); +} + +int main(int argc, char** argv) { + char command; + const struct CMUnitTest readonly_tests_without_initialization[] = { + /* Test card events on slot */ + cmocka_unit_test_setup_teardown(wait_test, + token_initialize, token_cleanup), + + /* Check all the mechanisms provided by the token */ + cmocka_unit_test_setup_teardown(supported_mechanisms_test, + token_setup, token_cleanup), + + /* Complex readonly test of all objects on the card */ + cmocka_unit_test_setup_teardown(readonly_tests, + user_login_setup, after_test_cleanup), + + /* Multipart signatures and encryption */ + cmocka_unit_test_setup_teardown(multipart_tests, + user_login_setup, after_test_cleanup), + + /* Regression test Sign&Verify with various data lengths */ + cmocka_unit_test_setup_teardown(ec_sign_size_test, + user_login_setup, after_test_cleanup), + + /* Verify that the Usage flags on the objects are sane */ + cmocka_unit_test_setup_teardown(usage_test, + user_login_setup, after_test_cleanup), + + /* Verify that RSA-PSS and RSA-OAEP functions if supported */ + cmocka_unit_test_setup_teardown(pss_oaep_test, + user_login_setup, after_test_cleanup), + }; + + token.library_path = NULL; + token.pin = NULL; + token.pin_length = 0; + token.interactive = 0; + token.slot_id = (unsigned long) -1; + token.log.outfile = NULL; + + while ((command = getopt(argc, argv, "?hm:s:p:io:")) != -1) { + switch (command) { + case 'o': + token.log.outfile = strdup(optarg); + break; + case 'm': + token.library_path = strdup(optarg); + break; + case 's': + token.slot_id = atol(optarg); + break; + case 'p': + token.pin = (CK_UTF8CHAR*) strdup(optarg); + token.pin_length = strlen(optarg); + break; + case 'i': + token.interactive = 1; + break; + case 'h': + case '?': + display_usage(); + return 0; + default: + break; + } + } + + if (token.library_path == NULL) { + debug_print("Falling back to the default library " DEFAULT_P11LIB); + token.library_path = strdup(DEFAULT_P11LIB); + } + + if (token.pin == NULL || token.pin_length == 0) { + printf("No PIN specified. Please, specify it on command-line using -p switch\n"); + return -1; + } + + debug_print("Card info:\n\tPIN %s\n\tPIN LENGTH %lu\n\t", + token.pin, token.pin_length); + + return cmocka_run_group_tests(readonly_tests_without_initialization, + group_setup, group_teardown); +} + diff --git a/src/tests/p11test/p11test.supp b/src/tests/p11test/p11test.supp new file mode 100644 index 00000000..2d4473c3 --- /dev/null +++ b/src/tests/p11test/p11test.supp @@ -0,0 +1,30 @@ +{ + Suppress pcsc_detect_readers() + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + obj:* + obj:* + fun:pcsc_detect_readers + fun:sc_ctx_detect_readers + fun:sc_context_create + fun:C_Initialize +} +{ + Suppress MessageSend() errors + Memcheck:Param + socketcall.sendto(msg) + fun:send + fun:MessageSend + fun:MessageSendWithHeader + fun:SCardConnect + fun:pcsc_detect_readers + fun:sc_ctx_detect_readers + fun:sc_context_create + fun:C_Initialize + fun:load_pkcs11_module + fun:group_setup + obj:/usr/lib64/libcmocka.so.0.3.1 + fun:_cmocka_run_group_tests +} + diff --git a/src/tests/p11test/p11test_case_common.c b/src/tests/p11test/p11test_case_common.c new file mode 100644 index 00000000..0f89cbb5 --- /dev/null +++ b/src/tests/p11test/p11test_case_common.c @@ -0,0 +1,728 @@ +/* + * p11test_case_common.c: Functions shared between test cases. + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" + +char name_buffer[11]; +char flag_buffer[11]; + +/** + * If the object enforces re-authentication, do it now. + */ +void always_authenticate(test_cert_t *o, token_info_t *info) +{ + CK_RV rv; + if (!o->always_auth) + return; + + rv = info->function_pointer->C_Login(info->session_handle, + CKU_CONTEXT_SPECIFIC, info->pin, info->pin_length); + if (rv != CKR_OK) { + fail_msg(" [ SKIP %s ] Re-authentication failed", o->id_str); + } +} + +/** + * Allocate new place for next certificate to store in the list + * and return pointer to this object + */ +test_cert_t * +add_object(test_certs_t *objects, CK_ATTRIBUTE key_id, CK_ATTRIBUTE label) +{ + test_cert_t *o = NULL; + objects->count = objects->count+1; + objects->data = realloc(objects->data, objects->count * sizeof(test_cert_t)); + if (objects->data == NULL) + return NULL; + + o = &(objects->data[objects->count - 1]); + o->private_handle = CK_INVALID_HANDLE; + o->public_handle = CK_INVALID_HANDLE; + o->always_auth = 0; + o->bits = 0; + o->verify_public = 0; + o->num_mechs = 0; + o->type = -1; + o->sign = 0; + o->verify = 0; + o->decrypt = 0; + o->encrypt = 0; + o->wrap = 0; + o->unwrap = 0; + o->derive_priv = 0; + o->derive_pub = 0; + o->key_type = -1; + o->x509 = NULL; /* The "reuse" capability of d2i_X509() is strongly discouraged */ + o->key.rsa = NULL; + o->key.ec = NULL; + + /* Store the passed CKA_ID and CKA_LABEL */ + o->key_id = malloc(key_id.ulValueLen); + memcpy(o->key_id, key_id.pValue, key_id.ulValueLen); + o->key_id_size = key_id.ulValueLen; + o->id_str = convert_byte_string(o->key_id, o->key_id_size); + o->label = malloc(label.ulValueLen + 1); + strncpy(o->label, label.pValue, label.ulValueLen); + o->label[label.ulValueLen] = '\0'; + + return o; +} + +/* + * Search for certificate in the list by ID and return pointer to it + */ +test_cert_t * search_certificate(test_certs_t *objects, CK_ATTRIBUTE *id) +{ + unsigned int i = 0; + + while (i < objects->count && (objects->data[i].key_id_size != id->ulValueLen || + memcmp(objects->data[i].key_id, id->pValue, id->ulValueLen) != 0)) + i++; + + if (i == objects->count) + return NULL; + + return &(objects->data[i]); +} + +static void +add_supported_mechs(test_cert_t *o) +{ + size_t i; + + if (o->type == EVP_PK_RSA) { + if (token.num_rsa_mechs > 0 ) { + /* Get supported mechanisms by token */ + o->num_mechs = token.num_rsa_mechs; + for (i = 0; i <= token.num_rsa_mechs; i++) { + o->mechs[i].mech = token.rsa_mechs[i].mech; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = + token.rsa_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_RSA_PKCS; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY + | CKF_ENCRYPT | CKF_DECRYPT; + } + } else if (o->type == EVP_PK_EC) { + if (token.num_ec_mechs > 0 ) { + o->num_mechs = token.num_ec_mechs; + for (i = 0; i <= token.num_ec_mechs; i++) { + o->mechs[i].mech = token.ec_mechs[i].mech; + o->mechs[i].result_flags = 0; + o->mechs[i].usage_flags = + token.ec_mechs[i].usage_flags; + } + } else { + /* Use the default list */ + o->num_mechs = 1; + o->mechs[0].mech = CKM_ECDSA; + o->mechs[0].result_flags = 0; + o->mechs[0].usage_flags = CKF_SIGN | CKF_VERIFY; + } + } +} + +/** + * Allocate place in the structure for every certificate found + * and store related information + */ +int callback_certificates(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned int template_size, CK_OBJECT_HANDLE object_handle) +{ + EVP_PKEY *evp = NULL; + const u_char *cp; + test_cert_t *o = NULL; + + if ((o = add_object(objects, template[0], template[2])) == NULL) + return -1; + + /* Extract public key from the certificate */ + cp = template[1].pValue; + if (d2i_X509(&(o->x509), &cp, template[1].ulValueLen) == NULL) { + fail_msg("d2i_X509"); + } else if ((evp = X509_get_pubkey(o->x509)) == NULL) { + fail_msg("X509_get_pubkey failed."); + } + + if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) { + /* Extract public RSA key */ + RSA *rsa = EVP_PKEY_get0_RSA(evp); + if ((o->key.rsa = RSAPublicKey_dup(rsa)) == NULL) + fail_msg("RSAPublicKey_dup failed"); + o->type = EVP_PK_RSA; + o->bits = EVP_PKEY_bits(evp); + + } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) { + /* Extract public EC key */ + EC_KEY *ec = EVP_PKEY_get0_EC_KEY(evp); + if ((o->key.ec = EC_KEY_dup(ec)) == NULL) + fail_msg("EC_KEY_dup failed"); + o->type = EVP_PK_EC; + o->bits = EVP_PKEY_bits(evp); + + } else { + fprintf(stderr, "[WARN %s ]evp->type = 0x%.4X (not RSA, EC)\n", + o->id_str, EVP_PKEY_id(evp)); + } + EVP_PKEY_free(evp); + + debug_print(" [ OK %s ] Certificate with label %s loaded successfully", + o->id_str, o->label); + return 0; +} + +/** + * Pair found private keys on the card with existing certificates + */ +int callback_private_keys(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned int template_size, CK_OBJECT_HANDLE object_handle) +{ + test_cert_t *o = NULL; + char *key_id; + + /* Search for already stored certificate with same ID */ + if ((o = search_certificate(objects, &(template[3]))) == NULL) { + key_id = convert_byte_string(template[3].pValue, + template[3].ulValueLen); + fprintf(stderr, "Can't find certificate for private key with ID %s\n", key_id); + free(key_id); + + fprintf(stderr, "Let's create a bogus structure without certificate data\n"); + if ((o = add_object(objects, template[3], template[7])) == NULL) + return -1; + } + + if (o->private_handle != CK_INVALID_HANDLE) { + key_id = convert_byte_string(template[3].pValue, template[3].ulValueLen); + fprintf(stderr, "Object already filled? ID %s\n", key_id); + free(key_id); + return -1; + } + + /* Store attributes, flags and handles */ + o->private_handle = object_handle; + o->sign = (template[0].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[0].pValue) : CK_FALSE; + o->decrypt = (template[1].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[1].pValue) : CK_FALSE; + o->key_type = (template[2].ulValueLen != (CK_ULONG) -1) + ? *((CK_KEY_TYPE *) template[2].pValue) : (CK_KEY_TYPE) -1; + o->always_auth = (template[4].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[4].pValue) : CK_FALSE; + o->unwrap = (template[5].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[5].pValue) : CK_FALSE; + o->derive_priv = (template[6].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[6].pValue) : CK_FALSE; + + debug_print(" [ OK %s ] Private key loaded successfully S:%d D:%d T:%02lX", + o->id_str, o->sign, o->decrypt, o->key_type); + return 0; +} + +/** + * Pair found public keys on the card with existing certificates + */ +int callback_public_keys(test_certs_t *objects, + CK_ATTRIBUTE template[], unsigned int template_size, CK_OBJECT_HANDLE object_handle) +{ + test_cert_t *o = NULL; + char *key_id; + + /* Search for already stored certificate with same ID */ + if ((o = search_certificate(objects, &(template[3]))) == NULL) { + key_id = convert_byte_string(template[3].pValue, template[3].ulValueLen); + fprintf(stderr, "Can't find certificate for public key with ID %s\n", key_id); + free(key_id); + return -1; + } + + if (o->verify_public != 0) { + key_id = convert_byte_string(template[3].pValue, template[3].ulValueLen); + fprintf(stderr, "Object already filled? ID %s\n", key_id); + free(key_id); + return -1; + } + + o->public_handle = object_handle; + o->verify = (template[0].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[0].pValue) : CK_FALSE; + o->encrypt = (template[1].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[1].pValue) : CK_FALSE; + /* store key type in case there is no corresponding private key */ + o->key_type = (template[2].ulValueLen != (CK_ULONG) -1) + ? *((CK_KEY_TYPE *) template[2].pValue) : (CK_KEY_TYPE) -1; + o->wrap = (template[8].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[8].pValue) : CK_FALSE; + o->derive_pub = (template[9].ulValueLen != (CK_ULONG) -1) + ? *((CK_BBOOL *) template[9].pValue) : CK_FALSE; + + /* check if we get the same public key as from the certificate */ + if (o->key_type == CKK_RSA) { + BIGNUM *n = NULL, *e = NULL; + n = BN_bin2bn(template[4].pValue, template[4].ulValueLen, NULL); + e = BN_bin2bn(template[5].pValue, template[5].ulValueLen, NULL); + if (o->key.rsa != NULL) { + const BIGNUM *cert_n = NULL, *cert_e = NULL; + RSA_get0_key(o->key.rsa, &cert_n, &cert_e, NULL); + if (BN_cmp(cert_n, n) != 0 || + BN_cmp(cert_e, e) != 0) { + debug_print(" [WARN %s ] Got different public key then from the certificate", + o->id_str); + BN_free(n); + BN_free(e); + return -1; + } + BN_free(n); + BN_free(e); + o->verify_public = 1; + } else { /* store the public key for future use */ + o->type = EVP_PK_RSA; + o->key.rsa = RSA_new(); + RSA_set0_key(o->key.rsa, n, e, NULL); + n = NULL; + e = NULL; + } + } else if (o->key_type == CKK_EC) { + ASN1_OBJECT *oid = NULL; + ASN1_OCTET_STRING *s = NULL; + const unsigned char *pub, *p; + BIGNUM *bn = NULL; + EC_POINT *ecpoint; + EC_GROUP *ecgroup; + int nid, pub_len; + + /* Parse the nid out of the EC_PARAMS */ + p = template[6].pValue; + oid = d2i_ASN1_OBJECT(NULL, &p, template[6].ulValueLen); + nid = OBJ_obj2nid(oid); + ASN1_OBJECT_free(oid); + ecgroup = EC_GROUP_new_by_curve_name(nid); + EC_GROUP_set_asn1_flag(ecgroup, OPENSSL_EC_NAMED_CURVE); + + p = template[7].pValue; + s = d2i_ASN1_OCTET_STRING(NULL, &p, template[7].ulValueLen); + pub = ASN1_STRING_get0_data(s); + pub_len = ASN1_STRING_length(s); + bn = BN_bin2bn(pub, pub_len, NULL); + ASN1_STRING_free(s); + if (bn == NULL) { + debug_print(" [WARN %s ] Can not convert EC_POINT from" + "PKCS#11 to BIGNUM", o->id_str); + EC_GROUP_free(ecgroup); + return -1; + } + + ecpoint = EC_POINT_bn2point(ecgroup, bn, NULL, NULL); + BN_free(bn); + if (ecpoint == NULL) { + debug_print(" [WARN %s ] Can not convert EC_POINT from" + "BIGNUM to OpenSSL format", o->id_str); + EC_GROUP_free(ecgroup); + return -1; + } + + if (o->key.ec != NULL) { + const EC_GROUP *cert_group = EC_KEY_get0_group(o->key.ec); + const EC_POINT *cert_point = EC_KEY_get0_public_key(o->key.ec); + int cert_nid = EC_GROUP_get_curve_name(cert_group); + + if (cert_nid != nid || + EC_GROUP_cmp(cert_group, ecgroup, NULL) != 0 || + EC_POINT_cmp(ecgroup, cert_point, ecpoint, NULL) != 0) { + debug_print(" [WARN %s ] Got different public" + "key then from the certificate", + o->id_str); + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + return -1; + } + EC_GROUP_free(ecgroup); + EC_POINT_free(ecpoint); + o->verify_public = 1; + } else { /* store the public key for future use */ + o->type = EVP_PK_EC; + o->key.ec = EC_KEY_new_by_curve_name(nid); + EC_KEY_set_public_key(o->key.ec, ecpoint); + EC_KEY_set_group(o->key.ec, ecgroup); + } + } else { + debug_print(" [WARN %s ] non-RSA, non-EC key. Key type: %02lX", + o->id_str, o->key_type); + return -1; + } + + add_supported_mechs(o); + + debug_print(" [ OK %s ] Public key loaded successfully V:%d E:%d T:%02lX", + o->id_str, o->verify, o->encrypt, o->key_type); + return 0; +} + +int search_objects(test_certs_t *objects, token_info_t *info, + CK_ATTRIBUTE filter[], CK_LONG filter_size, CK_ATTRIBUTE template[], CK_LONG template_size, + int (*callback)(test_certs_t *, CK_ATTRIBUTE[], unsigned int, CK_OBJECT_HANDLE)) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_ULONG object_count; + CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE_PTR object_handles = NULL; + unsigned long i = 0, objects_length = 0; + int j; + + /* FindObjects first + * https://wiki.oasis-open.org/pkcs11/CommonBugs + */ + rv = fp->C_FindObjectsInit(info->session_handle, filter, filter_size); + if (rv != CKR_OK) { + fprintf(stderr, "C_FindObjectsInit: rv = 0x%.8lX\n", rv); + return -1; + } + + while(1) { + rv = fp->C_FindObjects(info->session_handle, &object_handle, 1, &object_count); + if (object_count == 0) + break; + if (rv != CKR_OK) { + fprintf(stderr, "C_FindObjects: rv = 0x%.8lX\n", rv); + return -1; + } + /* store handle */ + if (i >= objects_length) { + objects_length += 4; // do not realloc after each row + object_handles = realloc(object_handles, objects_length * sizeof(CK_OBJECT_HANDLE_PTR)); + if (object_handles == NULL) + fail_msg("Realloc failed. Need to store object handles.\n"); + } + object_handles[i++] = object_handle; + } + objects_length = i; //terminate list of handles + + rv = fp->C_FindObjectsFinal(info->session_handle); + if (rv != CKR_OK) { + fprintf(stderr, "C_FindObjectsFinal: rv = 0x%.8lX\n", rv); + fail_msg("Could not find certificate.\n"); + } + + for (i = 0; i < objects_length; i++) { + /* Find attributes one after another to handle errors + * https://wiki.oasis-open.org/pkcs11/CommonBugs + */ + for (j = 0; j < template_size; j++) { + template[j].pValue = NULL; + template[j].ulValueLen = 0; + + rv = fp->C_GetAttributeValue(info->session_handle, object_handles[i], + &(template[j]), 1); + if (rv == CKR_ATTRIBUTE_TYPE_INVALID) + continue; + else if (rv != CKR_OK) + fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv); + + /* Allocate memory to hold the data we want */ + if (template[j].ulValueLen == 0) { + continue; + } else { + template[j].pValue = malloc(template[j].ulValueLen); + if (template[j].pValue == NULL) + fail_msg("malloc failed"); + } + /* Call again to get actual attribute */ + rv = fp->C_GetAttributeValue(info->session_handle, object_handles[i], + &(template[j]), 1); + if (rv != CKR_OK) + fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv); + } + + callback(objects, template, template_size, object_handles[i]); + // XXX check results + for (j = 0; j < template_size; j++) + free(template[j].pValue); + } + free(object_handles); + return 0; +} + +void search_for_all_objects(test_certs_t *objects, token_info_t *info) +{ + CK_OBJECT_CLASS keyClass = CKO_CERTIFICATE; + CK_OBJECT_CLASS privateClass = CKO_PRIVATE_KEY; + CK_OBJECT_CLASS publicClass = CKO_PUBLIC_KEY; + CK_ATTRIBUTE filter[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + }; + CK_ULONG filter_size = 1; + CK_ATTRIBUTE attrs[] = { + { CKA_ID, NULL_PTR, 0}, + { CKA_VALUE, NULL_PTR, 0}, + { CKA_LABEL, NULL_PTR, 0}, + { CKA_CERTIFICATE_TYPE, NULL_PTR, 0}, + }; + CK_ULONG attrs_size = sizeof (attrs) / sizeof (CK_ATTRIBUTE); + CK_ATTRIBUTE private_attrs[] = { + { CKA_SIGN, NULL, 0}, // CK_BBOOL + { CKA_DECRYPT, NULL, 0}, // CK_BBOOL + { CKA_KEY_TYPE, NULL, 0}, // CKK_ + { CKA_ID, NULL, 0}, + { CKA_ALWAYS_AUTHENTICATE, NULL, 0}, // CK_BBOOL + { CKA_UNWRAP, NULL, 0}, // CK_BBOOL + { CKA_DERIVE, NULL, 0}, // CK_BBOOL + { CKA_LABEL, NULL_PTR, 0}, + }; + CK_ULONG private_attrs_size = sizeof (private_attrs) / sizeof (CK_ATTRIBUTE); + CK_ATTRIBUTE public_attrs[] = { + { CKA_VERIFY, NULL, 0}, // CK_BBOOL + { CKA_ENCRYPT, NULL, 0}, // CK_BBOOL + { CKA_KEY_TYPE, NULL, 0}, + { CKA_ID, NULL, 0}, + { CKA_MODULUS, NULL, 0}, + { CKA_PUBLIC_EXPONENT, NULL, 0}, + { CKA_EC_PARAMS, NULL, 0}, + { CKA_EC_POINT, NULL, 0}, + { CKA_WRAP, NULL, 0}, // CK_BBOOL + { CKA_DERIVE, NULL, 0}, // CK_BBOOL + }; + CK_ULONG public_attrs_size = sizeof (public_attrs) / sizeof (CK_ATTRIBUTE); + + debug_print("\nSearch for all certificates on the card"); + search_objects(objects, info, filter, filter_size, + attrs, attrs_size, callback_certificates); + + + /* do the same thing with private keys (collect handles based on the collected IDs) */ + debug_print("\nSearch for all private keys respective to the certificates"); + filter[0].pValue = &privateClass; + // search for all and pair on the fly + search_objects(objects, info, filter, filter_size, + private_attrs, private_attrs_size, callback_private_keys); + + debug_print("\nSearch for all public keys respective to the certificates"); + filter[0].pValue = &publicClass; + search_objects(objects, info, filter, filter_size, + public_attrs, public_attrs_size, callback_public_keys); +} + +void clean_all_objects(test_certs_t *objects) { + unsigned int i; + for (i = 0; i < objects->count; i++) { + free(objects->data[i].key_id); + free(objects->data[i].id_str); + free(objects->data[i].label); + X509_free(objects->data[i].x509); + if (objects->data[i].key_type == CKK_RSA && + objects->data[i].key.rsa != NULL) + RSA_free(objects->data[i].key.rsa); + else if (objects->data[i].key.ec != NULL) + EC_KEY_free(objects->data[i].key.ec); + } + free(objects->data); +} + +const char *get_mechanism_name(int mech_id) +{ + switch (mech_id) { + case CKM_RSA_PKCS: + return "RSA_PKCS"; + case CKM_SHA1_RSA_PKCS: + return "SHA1_RSA_PKCS"; + case CKM_SHA224_RSA_PKCS: + return "SHA224_RSA_PKCS"; + case CKM_SHA256_RSA_PKCS: + return "SHA256_RSA_PKCS"; + case CKM_SHA384_RSA_PKCS: + return "SHA384_RSA_PKCS"; + case CKM_SHA512_RSA_PKCS: + return "SHA512_RSA_PKCS"; + case CKM_RSA_X_509: + return "RSA_X_509"; + case CKM_ECDSA: + return "ECDSA"; + case CKM_ECDSA_SHA1: + return "ECDSA_SHA1"; + case CKM_ECDSA_SHA256: + return "ECDSA_SHA256"; + case CKM_ECDSA_SHA384: + return "ECDSA_SHA384"; + case CKM_ECDSA_SHA512: + return "ECDSA_SHA512"; + case CKM_ECDH1_DERIVE: + return "ECDH1_DERIVE"; + case CKM_ECDH1_COFACTOR_DERIVE: + return "ECDH1_COFACTOR_DERIVE"; + case CKM_EC_KEY_PAIR_GEN: + return "EC_KEY_PAIR_GEN"; + case CKM_RSA_PKCS_KEY_PAIR_GEN: + return "RSA_PKCS_KEY_PAIR_GEN"; + case CKM_MD5_RSA_PKCS: + return "MD5_RSA_PKCS"; + case CKM_RIPEMD160_RSA_PKCS: + return "RIPEMD160_RSA_PKCS"; + case CKM_RSA_PKCS_PSS: + return "RSA_PKCS_PSS"; + case CKM_SHA1_RSA_PKCS_PSS: + return "SHA1_RSA_PKCS_PSS"; + case CKM_SHA256_RSA_PKCS_PSS: + return "SHA256_RSA_PKCS_PSS"; + case CKM_SHA384_RSA_PKCS_PSS: + return "SHA384_RSA_PKCS_PSS"; + case CKM_SHA512_RSA_PKCS_PSS: + return "SHA512_RSA_PKCS_PSS"; + case CKM_SHA_1_HMAC: + return "SHA_1_HMAC"; + case CKM_SHA256_HMAC: + return "SHA256_HMAC"; + case CKM_SHA384_HMAC: + return "SHA384_HMAC"; + case CKM_SHA512_HMAC: + return "SHA512_HMAC"; + case CKM_RSA_PKCS_OAEP: + return "RSA_PKCS_OAEP"; + case CKM_SHA_1: + return "SHA_1"; + case CKM_SHA224: + return "SHA224"; + case CKM_SHA256: + return "SHA256"; + case CKM_SHA384: + return "SHA384"; + case CKM_SHA512: + return "SHA512"; + default: + sprintf(name_buffer, "0x%.8X", mech_id); + return name_buffer; + } +} + +const char *get_mgf_name(int mgf_id) +{ + switch (mgf_id) { + case CKG_MGF1_SHA1: + return "MGF1_SHA_1"; + case CKG_MGF1_SHA224: + return "MGF1_SHA224"; + case CKG_MGF1_SHA256: + return "MGF1_SHA256"; + case CKG_MGF1_SHA384: + return "MGF1_SHA384"; + case CKG_MGF1_SHA512: + return "MGF1_SHA512"; + default: + sprintf(name_buffer, "0x%.8X", mgf_id); + return name_buffer; + } +} + +const char *get_mechanism_flag_name(int mech_id) +{ + switch (mech_id) { + case CKF_HW: + return "CKF_HW"; + case CKF_ENCRYPT: + return "CKF_ENCRYPT"; + case CKF_DECRYPT: + return "CKF_DECRYPT"; + case CKF_DIGEST: + return "CKF_DIGEST"; + case CKF_SIGN: + return "CKF_SIGN"; + case CKF_SIGN_RECOVER: + return "CKF_SIGN_RECOVER"; + case CKF_VERIFY: + return "CKF_VERIFY"; + case CKF_VERIFY_RECOVER: + return "CKF_VERIFY_RECOVER"; + case CKF_GENERATE: + return "CKF_GENERATE"; + case CKF_GENERATE_KEY_PAIR: + return "CKF_GENERATE_KEY_PAIR"; + case CKF_WRAP: + return "CKF_WRAP"; + case CKF_UNWRAP: + return "CKF_UNWRAP"; + case CKF_DERIVE: + return "CKF_DERIVE"; + case CKF_EC_F_P: + return "CKF_EC_F_P"; + case CKF_EC_F_2M: + return "CKF_EC_F_2M"; + case CKF_EC_NAMEDCURVE: + return "CKF_EC_NAMEDCURVE"; + case CKF_EC_UNCOMPRESS: + return "CKF_EC_UNCOMPRESS"; + case CKF_EC_COMPRESS: + return "CKF_EC_COMPRESS"; + case CKF_EC_ECPARAMETERS: + return "CKF_EC_ECPARAMETERS"; + default: + sprintf(flag_buffer, "0x%.8X", mech_id); + return flag_buffer; + } +} + +char *convert_byte_string(unsigned char *id, unsigned long length) +{ + unsigned int i; + char *data = malloc(3 * length * sizeof(char) + 1); + for (i = 0; i < length; i++) + sprintf(&data[i*3], "%02X:", id[i]); + data[length*3-1] = '\0'; + return data; +} + +void write_data_row(token_info_t *info, int cols, ...) +{ + va_list ap; + int i, intval, type; + char *data; + + cols = cols*2; /* shut GCC up */ + va_start(ap, cols); + fprintf(info->log.fd, "\n\t["); + for (i = 1; i <= cols; i+=2) { + if (i > 1) + fprintf(info->log.fd, ","); + type = va_arg(ap, int); + if (type == 'd') { + intval = va_arg(ap, int); + fprintf(info->log.fd, "\n\t\t\"%d\"", intval); + } else if (type == 's') { + data = va_arg(ap, char*); + fprintf(info->log.fd, "\n\t\t\"%s\"", data); + } + } + fprintf(info->log.fd, "\n\t]"); + va_end(ap); +} + +int is_pss_mechanism(CK_MECHANISM_TYPE mech) +{ + return (mech == CKM_RSA_PKCS_PSS + || mech == CKM_SHA1_RSA_PKCS_PSS + || mech == CKM_SHA256_RSA_PKCS_PSS + || mech == CKM_SHA384_RSA_PKCS_PSS + || mech == CKM_SHA512_RSA_PKCS_PSS + || mech == CKM_SHA224_RSA_PKCS_PSS); +} diff --git a/src/tests/p11test/p11test_case_common.h b/src/tests/p11test/p11test_case_common.h new file mode 100644 index 00000000..b9171ab5 --- /dev/null +++ b/src/tests/p11test/p11test_case_common.h @@ -0,0 +1,127 @@ +/* + * p11test_case_common.h: Functions shared between test cases. + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#ifndef P11TEST_CASE_COMMON_H +#define P11TEST_CASE_COMMON_H + +#include +#include +#include +#include "p11test_common.h" + +typedef struct { + unsigned char *key_id; + CK_ULONG key_id_size; + char *id_str; + X509 *x509; + int type; + union { + RSA *rsa; + EC_KEY *ec; + } key; + CK_OBJECT_HANDLE private_handle; + CK_OBJECT_HANDLE public_handle; + CK_BBOOL sign; + CK_BBOOL decrypt; + CK_BBOOL verify; + CK_BBOOL encrypt; + CK_BBOOL wrap; + CK_BBOOL unwrap; + CK_BBOOL derive_priv; + CK_BBOOL derive_pub; + CK_KEY_TYPE key_type; + CK_BBOOL always_auth; + char *label; + CK_ULONG bits; + int verify_public; + test_mech_t mechs[MAX_MECHS]; + int num_mechs; +} test_cert_t; + +typedef struct { + unsigned int count; + test_cert_t *data; +} test_certs_t; + +void always_authenticate(test_cert_t *o, token_info_t *info); + +int search_objects(test_certs_t *objects, token_info_t *info, + CK_ATTRIBUTE filter[], CK_LONG filter_size, CK_ATTRIBUTE template[], CK_LONG template_size, + int (*callback)(test_certs_t *, CK_ATTRIBUTE[], unsigned int, CK_OBJECT_HANDLE)); +void search_for_all_objects(test_certs_t *objects, token_info_t *info); +void clean_all_objects(test_certs_t *objects); + +const char *get_mechanism_name(int mech_id); +const char *get_mgf_name(int mech_id); +const char *get_mechanism_flag_name(int flag_id); +char *convert_byte_string(unsigned char *id, unsigned long length); + +int is_pss_mechanism(CK_MECHANISM_TYPE mech); + +// TODO sanitize inputs + +#define P11TEST_START(info) if (info->log.fd) { \ + if (info->log.in_test) \ + fprintf(info->log.fd, "\n\t\"result\": \"unknown\"\n}"); \ + fprintf(info->log.fd, "%s\n{\n\t\"test_id\": \"%s\"", \ + info->log.first ? "" : ",", __func__); \ + info->log.in_test = 1; \ + info->log.first = 0; \ + info->log.in_data = 0; \ + } else {} + +#define _P11TEST_FINALIZE(info, result) if (info->log.fd) {\ + if (info->log.in_data) {\ + fprintf(info->log.fd, "]"); \ + } \ + if (info->log.fd && info->log.in_test) { \ + fprintf(info->log.fd, ",\n\t\"result\": \"" result "\"\n}"); \ + info->log.in_test = 0; \ + } \ + } else {} + +#define P11TEST_SKIP(info) do { _P11TEST_FINALIZE(info, "skip") skip(); } while(0); + +#define P11TEST_PASS(info) do { _P11TEST_FINALIZE(info, "pass") } while(0); + +#define P11TEST_FAIL(info, msg, ...) do { \ + if (info->log.fd && info->log.in_test) { \ + fprintf(info->log.fd, ",\n\t\"fail_reason\": \"" msg "\"", ##__VA_ARGS__); \ + } \ + _P11TEST_FINALIZE(info, "fail") \ + fail_msg(msg, ##__VA_ARGS__); \ + } while (0); + +#define P11TEST_DATA_ROW(info, cols, ...) if (info->log.fd) { \ + if (info->log.in_test == 0) \ + fail_msg("Can't add data outside of the test");\ + if (info->log.in_data == 0) {\ + fprintf(info->log.fd, ",\n\t\"data\": [");\ + info->log.in_data = 1;\ + } else { \ + fprintf(info->log.fd, ",");\ + } \ + write_data_row(info, cols, ##__VA_ARGS__); \ + } else {} + +void write_data_row(token_info_t *info, int cols, ...); + +#endif /* P11TEST_CASE_COMMON_H */ diff --git a/src/tests/p11test/p11test_case_ec_sign.c b/src/tests/p11test/p11test_case_ec_sign.c new file mode 100644 index 00000000..eed3f338 --- /dev/null +++ b/src/tests/p11test/p11test_case_ec_sign.c @@ -0,0 +1,64 @@ +/* + * p11test_case_ec_sign.c: Test different data lengths for EC signatures + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "p11test_case_ec_sign.h" + +void ec_sign_size_test(void **state) { + unsigned int i; + int min, max, j, l, errors = 0, rv; + token_info_t *info = (token_info_t *) *state; + + P11TEST_START(info); + if (token.num_ec_mechs == 0 ) { + fprintf(stderr, "Token does not support any ECC mechanisms. Skipping.\n"); + skip(); + } + + test_certs_t objects; + objects.count = 0; + objects.data = NULL; + + search_for_all_objects(&objects, info); + + debug_print("\nCheck functionality of Sign&Verify on different data lengths"); + for (i = 0; i < objects.count; i++) { + if (objects.data[i].key_type != CKK_EC) + continue; + // sanity: Test all mechanisms + min = (objects.data[i].bits + 7) / 8 - 2; + max = (objects.data[i].bits + 7) / 8 + 2; + if (objects.data[i].sign && objects.data[i].verify) { + for (j = 0; j < objects.data[i].num_mechs; j++) { + for (l = min; l < max; l++) { + rv = sign_verify_test(&(objects.data[i]), info, + &(objects.data[i].mechs[j]), l, 0); + if (rv == -1) + errors++; + } + } + } + } + clean_all_objects(&objects); + + if (errors > 0) + P11TEST_FAIL(info, "Some signatures were not verified successfully. Please review the log"); + P11TEST_PASS(info); +} + diff --git a/src/tests/p11test/p11test_case_ec_sign.h b/src/tests/p11test/p11test_case_ec_sign.h new file mode 100644 index 00000000..299a92f2 --- /dev/null +++ b/src/tests/p11test/p11test_case_ec_sign.h @@ -0,0 +1,25 @@ +/* + * p11test_case_ec_sign.h: Test different data lengths for EC signatures + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void ec_sign_size_test(void **state); diff --git a/src/tests/p11test/p11test_case_mechs.c b/src/tests/p11test/p11test_case_mechs.c new file mode 100644 index 00000000..b8b31ee3 --- /dev/null +++ b/src/tests/p11test/p11test_case_mechs.c @@ -0,0 +1,138 @@ +/* + * p11test_case_mechs.c: Check mechanisms supported by token + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_mechs.h" + +void supported_mechanisms_test(void **state) { + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + CK_RV rv; + CK_ULONG mechanism_count, i; + CK_MECHANISM_TYPE_PTR mechanism_list; + CK_MECHANISM_INFO_PTR mechanism_info; + CK_FLAGS j; + test_mech_t *mech = NULL; + + P11TEST_START(info); + rv = function_pointer->C_GetMechanismList(info->slot_id, NULL_PTR, + &mechanism_count); + if ((rv == CKR_OK) && (mechanism_count > 0)) { + mechanism_list = (CK_MECHANISM_TYPE_PTR) + malloc(mechanism_count * sizeof(CK_MECHANISM_TYPE)); + rv = function_pointer->C_GetMechanismList(info->slot_id, + mechanism_list, &mechanism_count); + if (rv != CKR_OK) { + free(mechanism_list); + function_pointer->C_Finalize(NULL_PTR); + P11TEST_FAIL(info, "Could not get mechanism list!"); + } + + mechanism_info = (CK_MECHANISM_INFO_PTR) + malloc(mechanism_count * sizeof(CK_MECHANISM_INFO)); + if (mechanism_info == NULL) + P11TEST_FAIL(info, "Couldn't malloc()"); + + for (i = 0; i < mechanism_count; i++) { + CK_MECHANISM_TYPE mechanism_type = mechanism_list[i]; + rv = function_pointer->C_GetMechanismInfo(info->slot_id, + mechanism_type, &mechanism_info[i]); + if (rv != CKR_OK) + continue; + + /* store mechanisms list for later tests */ + + /* List all known RSA mechanisms */ + if (mechanism_list[i] == CKM_RSA_X_509 + || mechanism_list[i] == CKM_RSA_PKCS + || mechanism_list[i] == CKM_MD5_RSA_PKCS + || mechanism_list[i] == CKM_RIPEMD160_RSA_PKCS + || mechanism_list[i] == CKM_SHA1_RSA_PKCS + || mechanism_list[i] == CKM_SHA224_RSA_PKCS + || mechanism_list[i] == CKM_SHA256_RSA_PKCS + || mechanism_list[i] == CKM_SHA384_RSA_PKCS + || mechanism_list[i] == CKM_SHA512_RSA_PKCS + || mechanism_list[i] == CKM_RSA_PKCS_PSS + || mechanism_list[i] == CKM_SHA1_RSA_PKCS_PSS + || mechanism_list[i] == CKM_SHA256_RSA_PKCS_PSS + || mechanism_list[i] == CKM_SHA384_RSA_PKCS_PSS + || mechanism_list[i] == CKM_SHA512_RSA_PKCS_PSS + || mechanism_list[i] == CKM_RSA_PKCS_OAEP) { + if (token.num_rsa_mechs < MAX_MECHS) { + mech = &token.rsa_mechs[token.num_rsa_mechs++]; + mech->mech = mechanism_list[i]; + mech->usage_flags = mechanism_info[i].flags; + } else + P11TEST_FAIL(info, "Too many RSA mechanisms (%d)", MAX_MECHS); + } + + /* We list all known EC mechanisms */ + if (mechanism_list[i] == CKM_ECDSA + || mechanism_list[i] == CKM_ECDSA_SHA1 + || mechanism_list[i] == CKM_ECDSA_SHA256 + || mechanism_list[i] == CKM_ECDSA_SHA384 + || mechanism_list[i] == CKM_ECDSA_SHA512) { + if (token.num_ec_mechs < MAX_MECHS) { + mech = &token.ec_mechs[token.num_ec_mechs++]; + mech->mech = mechanism_list[i]; + mech->usage_flags = mechanism_info[i].flags; + } else + P11TEST_FAIL(info, "Too many EC mechanisms (%d)", MAX_MECHS); + } + if ((mechanism_info[i].flags & CKF_GENERATE_KEY_PAIR) != 0) { + if (token.num_keygen_mechs < MAX_MECHS) { + mech = &token.keygen_mechs[token.num_keygen_mechs++]; + mech->mech = mechanism_list[i]; + mech->usage_flags = mechanism_info[i].flags; + } else + P11TEST_FAIL(info, "Too many KEYGEN mechanisms (%d)", MAX_MECHS); + } + } + + printf("[ MECHANISM ] [ KEY SIZE ] [ FLAGS ]\n"); + printf("[ CKM_* ] [ MIN][ MAX] [ ]\n"); + P11TEST_DATA_ROW(info, 4, + 's', "MECHANISM", + 's', "MIN KEY", + 's', "MAX KEY", + 's', "FLAGS"); + for (i = 0; i < mechanism_count; i++) { + printf("[%-21s] [%4lu][%4lu] [%10s]", + get_mechanism_name(mechanism_list[i]), + mechanism_info[i].ulMinKeySize, + mechanism_info[i].ulMaxKeySize, + get_mechanism_flag_name(mechanism_info[i].flags)); + P11TEST_DATA_ROW(info, 4, + 's', get_mechanism_name(mechanism_list[i]), + 'd', mechanism_info[i].ulMinKeySize, + 'd', mechanism_info[i].ulMaxKeySize, + 's', get_mechanism_flag_name(mechanism_info[i].flags)); + for (j = 1; j <= CKF_EC_COMPRESS; j = j<<1) + if ((mechanism_info[i].flags & j) != 0) + printf(" %s", get_mechanism_flag_name(j)); + printf("\n"); + } + free(mechanism_list); + free(mechanism_info); + } + P11TEST_PASS(info); +} + diff --git a/src/tests/p11test/p11test_case_mechs.h b/src/tests/p11test/p11test_case_mechs.h new file mode 100644 index 00000000..ff3439cd --- /dev/null +++ b/src/tests/p11test/p11test_case_mechs.h @@ -0,0 +1,24 @@ +/* + * p11test_case_mechs.h: Check mechanisms supported by token + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" + +void supported_mechanisms_test(void **state); diff --git a/src/tests/p11test/p11test_case_multipart.c b/src/tests/p11test/p11test_case_multipart.c new file mode 100644 index 00000000..0aa45638 --- /dev/null +++ b/src/tests/p11test/p11test_case_multipart.c @@ -0,0 +1,107 @@ +/* + * p11test_case_multipart.c: Multipart Sign & Verify tests (RSA only) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_multipart.h" + +void multipart_tests(void **state) { + + token_info_t *info = (token_info_t *) *state; + unsigned int i; + int used, j; + test_certs_t objects; + + objects.count = 0; + objects.data = NULL; + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + debug_print("\nCheck functionality of Multipart Sign&Verify and/or Encrypt&Decrypt"); + for (i = 0; i < objects.count; i++) { + if (objects.data[i].type == EVP_PK_EC) { + debug_print(" [ SKIP %s ] EC keys do not support multi-part operations", + objects.data[i].id_str); + continue; + } + used = 0; + /* do the Sign&Verify and/or Encrypt&Decrypt */ + /* XXX some keys do not have appropriate flags, but we can use them + * or vice versa */ + //if (objects.data[i].sign && objects.data[i].verify) + for (j = 0; j < objects.data[i].num_mechs; j++) + used |= sign_verify_test(&(objects.data[i]), info, + &(objects.data[i].mechs[j]), 32, 1); + + if (!used) { + debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX", + objects.data[i].id_str, objects.data[i].key_type); + } + } + + if (objects.count == 0) { + printf(" [WARN] No objects to display\n"); + return; + } + + /* print summary */ + printf("[KEY ID] [TYPE] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [LABEL]\n"); + P11TEST_DATA_ROW(info, 3, + 's', "KEY ID", + 's', "MECHANISM", + 's', "MULTIPART SIGN&VERIFY WORKS"); + for (i = 0; i < objects.count; i++) { + if (objects.data[i].type == EVP_PK_EC) + continue; + printf("[%-6s] [%s] [%6lu] [ %s ] [%s%s] [%s]\n", + objects.data[i].id_str, + objects.data[i].key_type == CKK_RSA ? "RSA " : + objects.data[i].key_type == CKK_EC ? " EC " : " ?? ", + objects.data[i].bits, + objects.data[i].verify_public == 1 ? " ./ " : " ", + objects.data[i].sign ? "[./] " : "[ ] ", + objects.data[i].verify ? " [./] " : " [ ] ", + objects.data[i].label); + for (j = 0; j < objects.data[i].num_mechs; j++) { + test_mech_t *mech = &objects.data[i].mechs[j]; + if ((mech->usage_flags & CKF_SIGN) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-20s ] [ %s ]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_SIGN_ANY ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0) + continue; /* do not export unknown and non-working algorithms */ + P11TEST_DATA_ROW(info, 3, + 's', objects.data[i].id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_SIGN_ANY ? "YES" : ""); + } + printf("\n"); + } + printf(" Public == Cert ------------^ ^ ^ ^\n"); + printf(" Sign Attribute --------------------' | |\n"); + printf(" Sign&Verify functionality ------------' |\n"); + printf(" Verify Attribute ------------------------'\n"); + + clean_all_objects(&objects); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_multipart.h b/src/tests/p11test/p11test_case_multipart.h new file mode 100644 index 00000000..3ca6f97d --- /dev/null +++ b/src/tests/p11test/p11test_case_multipart.h @@ -0,0 +1,26 @@ +/* + * p11test_case_multipart.h: Multipart Sign & Verify tests (RSA only) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void multipart_tests(void **state); + diff --git a/src/tests/p11test/p11test_case_pss_oaep.c b/src/tests/p11test/p11test_case_pss_oaep.c new file mode 100644 index 00000000..d0b8392f --- /dev/null +++ b/src/tests/p11test/p11test_case_pss_oaep.c @@ -0,0 +1,874 @@ +/* + * p11test_case_pss_oaep.c: RSA-PSS and RSA-OAEP tests + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_pss_oaep.h" +#include "libopensc/internal.h" + +#include +#include + +#define SHORT_MESSAGE_TO_SIGN "Simple message for signing & verifying. It needs to be little bit longer to fit also longer keys and allow the truncation.\n" +#define BUFFER_SIZE 4096 + +const unsigned char *global_message = (unsigned char *) SHORT_MESSAGE_TO_SIGN; +size_t global_message_length = sizeof(SHORT_MESSAGE_TO_SIGN); + +const CK_MECHANISM_TYPE * +get_oaep_mechanism_hashes(CK_MECHANISM_TYPE mech) +{ + static CK_MECHANISM_TYPE h[6]; + + switch (mech) { + case CKM_RSA_PKCS_OAEP: + h[0] = CKM_SHA_1; + h[1] = CKM_SHA224; + h[2] = CKM_SHA256; + h[3] = CKM_SHA384; + h[4] = CKM_SHA512; + h[5] = -1; + break; + + default: + h[0] = -1; + break; + } + + return h; +} +const CK_MECHANISM_TYPE * +get_pss_mechanism_hashes(CK_MECHANISM_TYPE mech) +{ + static CK_MECHANISM_TYPE h[6]; + + switch (mech) { + case CKM_RSA_PKCS_PSS: + h[0] = CKM_SHA_1; + h[1] = CKM_SHA224; + h[2] = CKM_SHA256; + h[3] = CKM_SHA384; + h[4] = CKM_SHA512; + h[5] = -1; + break; + + case CKM_SHA1_RSA_PKCS_PSS: + h[0] = CKM_SHA_1; + h[1] = -1; + break; + + case CKM_SHA224_RSA_PKCS_PSS: + h[0] = CKM_SHA224; + h[1] = -1; + break; + + case CKM_SHA256_RSA_PKCS_PSS: + h[0] = CKM_SHA256; + h[1] = -1; + break; + + case CKM_SHA384_RSA_PKCS_PSS: + h[0] = CKM_SHA384; + h[1] = -1; + break; + + case CKM_SHA512_RSA_PKCS_PSS: + h[0] = CKM_SHA512; + h[1] = -1; + break; + + default: + h[0] = -1; + break; + } + + return h; +} + +const CK_MECHANISM_TYPE * +get_mechanism_hashes(CK_MECHANISM_TYPE mech) +{ + if (mech == CKM_RSA_PKCS_OAEP) + return get_oaep_mechanism_hashes(mech); + else + return get_pss_mechanism_hashes(mech); +} + +const CK_RSA_PKCS_MGF_TYPE * +get_mgfs(void) +{ + static CK_RSA_PKCS_MGF_TYPE h[6]; + h[0] = CKG_MGF1_SHA1; + h[1] = CKG_MGF1_SHA224; + h[2] = CKG_MGF1_SHA256; + h[3] = CKG_MGF1_SHA384; + h[4] = CKG_MGF1_SHA512; + h[5] = -1; + return h; +} + +const EVP_MD *mgf_cryptoki_to_ossl(CK_RSA_PKCS_MGF_TYPE mgf) +{ + switch (mgf) { + case CKG_MGF1_SHA224: + return EVP_sha224(); + + case CKG_MGF1_SHA256: + return EVP_sha256(); + + case CKG_MGF1_SHA384: + return EVP_sha384(); + + case CKG_MGF1_SHA512: + return EVP_sha512(); + + case CKG_MGF1_SHA1: + default: + return EVP_sha1(); + + } +} + +const EVP_MD *md_cryptoki_to_ossl(CK_MECHANISM_TYPE hash) +{ + /* Digest mechanisms */ + switch (hash) { + case CKM_SHA224: + return EVP_sha224(); + + case CKM_SHA256: + return EVP_sha256(); + + case CKM_SHA384: + return EVP_sha384(); + + case CKM_SHA512: + return EVP_sha512(); + + case CKM_SHA_1: + default: + return EVP_sha1(); + + } +} + +size_t get_hash_length(CK_MECHANISM_TYPE mech) +{ + switch (mech) { + case CKM_SHA224: + return SHA224_DIGEST_LENGTH; + case CKM_SHA256: + return SHA256_DIGEST_LENGTH; + case CKM_SHA384: + return SHA384_DIGEST_LENGTH; + case CKM_SHA512: + return SHA512_DIGEST_LENGTH; + default: + case CKM_SHA_1: + return SHA_DIGEST_LENGTH; + } +} + +CK_BYTE *hash_message(const CK_BYTE *message, size_t message_length, + CK_MECHANISM_TYPE hash) +{ + switch (hash) { + case CKM_SHA224: + return SHA224(message, message_length, NULL); + + case CKM_SHA256: + return SHA256(message, message_length, NULL); + + case CKM_SHA384: + return SHA384(message, message_length, NULL); + + case CKM_SHA512: + return SHA512(message, message_length, NULL); + + case CKM_SHA_1: + default: + return SHA1(message, message_length, NULL); + + } +} + +int oaep_encrypt_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + size_t enc_length = 0; + CK_RV rv = -1; + EVP_PKEY_CTX *pctx = NULL; + const EVP_MD *md = EVP_md_null(); + const EVP_MD *mgf1_md = EVP_md_null(); + EVP_PKEY *key = NULL; + + md = md_cryptoki_to_ossl(mech->hash); + mgf1_md = mgf_cryptoki_to_ossl(mech->mgf); + + if ((key = EVP_PKEY_new()) == NULL + || RSA_up_ref(o->key.rsa) < 1 + || EVP_PKEY_set1_RSA(key, o->key.rsa) != 1) { + fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + if ((pctx = EVP_PKEY_CTX_new(key, NULL)) == NULL + || EVP_PKEY_encrypt_init(pctx) != 1 + || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) != 1 + || EVP_PKEY_CTX_set_rsa_oaep_md(pctx, md) != 1 + || EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_md) != 1) { + fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + if (EVP_PKEY_encrypt(pctx, NULL, &enc_length, message, message_length) <= 0) { + fprintf(stderr, " [ ERROR %s ] Failed get signature length. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + *enc_message = OPENSSL_malloc(enc_length); + + rv = EVP_PKEY_encrypt(pctx, *enc_message, &enc_length, message, message_length); + if (rv <= 0) { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + } +out: + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(key); + return enc_length; +} + +void fill_oaep_params(CK_RSA_PKCS_OAEP_PARAMS *oaep_params, + test_mech_t *mech) +{ + oaep_params->hashAlg = mech->hash; + oaep_params->mgf = mech->mgf; + oaep_params->source = CKZ_DATA_SPECIFIED; + oaep_params->pSourceData = NULL; + oaep_params->ulSourceDataLen = 0; + +} + +int oaep_encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM enc_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_RSA_PKCS_OAEP_PARAMS oaep_params; + CK_ULONG enc_message_length; + static int encrypt_support = 1; + + fill_oaep_params(&oaep_params, mech); + enc_mechanism.pParameter = &oaep_params; + enc_mechanism.ulParameterLen = sizeof(oaep_params); + + if (!encrypt_support) + goto openssl_encrypt; + + rv = fp->C_EncryptInit(info->session_handle, &enc_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_EncryptInit: rv = 0x%.8lX", rv); + encrypt_support = 0; /* avoid trying over and over again */ + goto openssl_encrypt; + } + + /* get the expected length */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + NULL, &enc_message_length); + if (rv != CKR_OK) { + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + goto openssl_encrypt; + } + *enc_message = malloc(enc_message_length); + if (*enc_message == NULL) { + debug_print("malloc returned null"); + return -1; + } + + /* Do the actual encryption with allocated buffer */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + *enc_message, &enc_message_length); + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_DECRYPT_OPENSSL; + return enc_message_length; + } + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + +openssl_encrypt: + debug_print(" [ KEY %s ] Falling back to openssl encryption", o->id_str); + return oaep_encrypt_message_openssl(o, info, message, message_length, mech, + enc_message); +} + +int oaep_decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message, + CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM dec_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_RSA_PKCS_OAEP_PARAMS oaep_params; + CK_ULONG dec_message_length = BUFFER_SIZE; + + fill_oaep_params(&oaep_params, mech); + dec_mechanism.pParameter = &oaep_params; + dec_mechanism.ulParameterLen = sizeof(oaep_params); + + rv = fp->C_DecryptInit(info->session_handle, &dec_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to decrypt with this key?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print(" C_DecryptInit: rv = 0x%.8lX\n", rv); + return -1; + } + + *dec_message = malloc(dec_message_length); + + always_authenticate(o, info); + + rv = fp->C_Decrypt(info->session_handle, enc_message, + enc_message_length, *dec_message, &dec_message_length); + if (rv != CKR_OK) { + free(*dec_message); + debug_print(" C_Decrypt: rv = 0x%.8lX\n", rv); + return -1; + } + return (int) dec_message_length; +} + +/* Perform encryption and decryption of a message using private key referenced + * in the o object with mechanism defined by mech. + * + * NONE of the reasonable mechanisms support encryption/decryption + * + * Returns + * * 1 for successful Encrypt&Decrypt sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int oaep_encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ + CK_BYTE *message = (CK_BYTE *) SHORT_MESSAGE_TO_SIGN; + CK_BYTE *dec_message = NULL; + int dec_message_length = 0; + int message_length = 16; + unsigned char *enc_message; + int enc_message_length, rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PK_RSA) { + debug_print(" [ KEY %s ] Skip non-RSA key for encryption", o->id_str); + return 0; + } + + if (mech->mech != CKM_RSA_PKCS_OAEP) { + mech->usage_flags &= ~CKF_DECRYPT; + debug_print(" [SKIP %s ] non RSA-OAEP mechanism", o->id_str); + return 0; + } + + message_length = MIN((int)global_message_length, + (int)((o->bits+7)/8 - 2*get_hash_length(mech->hash) - 2)); + + /* will not work for 1024b RSA key and SHA512 hash: It has max size -2 */ + if (message_length < 0) { + mech->usage_flags &= ~CKF_DECRYPT; + debug_print(" [SKIP %s ] Too small modulus (%ld bits)" + " or too large hash %s (%lu B) for OAEP", o->id_str, + o->bits, get_mechanism_name(mech->hash), + get_hash_length(mech->hash)); + return 0; + } + + debug_print(" [ KEY %s ] Encrypt message of length %d using CKM_%s, " + "hash CKM_%s, mgf=CKG_%s", o->id_str, (unsigned) message_length, + get_mechanism_name(mech->mech), get_mechanism_name(mech->hash), + get_mgf_name(mech->mgf)); + enc_message_length = oaep_encrypt_message(o, info, message, + (unsigned) message_length, mech, &enc_message); + if (enc_message_length <= 0) { + return -1; + } + + debug_print(" [ KEY %s ] Decrypt message", o->id_str); + dec_message_length = oaep_decrypt_message(o, info, enc_message, + enc_message_length, mech, &dec_message); + free(enc_message); + if (dec_message_length <= 0) { + return -1; + } + + if (memcmp(dec_message, message, dec_message_length) == 0 + && dec_message_length == message_length) { + debug_print(" [ OK %s ] Text decrypted successfully.", o->id_str); + mech->result_flags |= FLAGS_DECRYPT; + rv = 1; + } else { + dec_message[dec_message_length] = '\0'; + debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s", + o->id_str, dec_message); + rv = 0; + } + free(dec_message); + return rv; +} + +static int get_max_salt_len(unsigned long bits, CK_MECHANISM_TYPE hash) +{ + return (bits + 7)/8 - get_hash_length(hash) - 2; +} + +int fill_pss_params(CK_RSA_PKCS_PSS_PARAMS *pss_params, + test_mech_t *mech, test_cert_t *o) +{ + pss_params->hashAlg = mech->hash; + pss_params->mgf = mech->mgf; + switch (mech->salt){ + case -2: + /* max possible ( modlen - hashlen -2 ) */ + pss_params->sLen = get_max_salt_len(o->bits,mech->hash); + break; + case -1: + /* digest length */ + /* will not work with SHA512 and 1024b keys (max is 62b!) */ + if ((int) get_hash_length(mech->hash) > get_max_salt_len(o->bits, mech->hash)) { + return -1; + } + pss_params->sLen = get_hash_length(mech->hash); + break; + case 0: + default: + pss_params->sLen = 0; + break; + } + return 1; +} + +int pss_sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **sign) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_ULONG sign_length = 0; + CK_RSA_PKCS_PSS_PARAMS pss_params; + + if (fill_pss_params(&pss_params, mech, o) != 1) { + debug_print(" [SKIP %s ] Impossible to use requested salt length", o->id_str); + return 0; + } + sign_mechanism.pParameter = &pss_params; + sign_mechanism.ulParameterLen = sizeof(pss_params); + + rv = fp->C_SignInit(info->session_handle, &sign_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str); + return 0; + } else if (rv == CKR_MECHANISM_INVALID) { + debug_print(" [SKIP %s ] Bad mechanism. Not supported?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print(" C_SignInit: rv = 0x%.8lX\n", rv); + return -1; + } + + always_authenticate(o, info); + + /* Call C_Sign with NULL argument to find out the real size of signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + if (rv != CKR_OK) { + fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv); + return -1; + } + + *sign = malloc(sign_length); + if (*sign == NULL) { + fprintf(stderr, "%s: malloc failed", __func__); + return -1; + } + + /* Call C_Sign with allocated buffer to the actual signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + + if (rv != CKR_OK) { + free(*sign); + fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv); + return -1; + } + return sign_length; +} + +int pss_verify_message_openssl(test_cert_t *o, token_info_t *info, + CK_BYTE *message, CK_ULONG message_length, test_mech_t *mech, + unsigned char *sign, CK_ULONG sign_length) +{ + CK_RV rv = -1; + EVP_PKEY_CTX *pctx = NULL; + const CK_BYTE *my_message; + CK_ULONG my_message_length; + const EVP_MD *mgf_md = EVP_md_null(); + const EVP_MD *md = EVP_md_null(); + EVP_PKEY *key = NULL; + + md = md_cryptoki_to_ossl(mech->hash); + mgf_md = mgf_cryptoki_to_ossl(mech->mgf); + + if (mech->mech != CKM_RSA_PKCS_PSS) { + my_message = hash_message(message, message_length, mech->hash); + my_message_length = get_hash_length(mech->hash); + } else { + my_message = message; + my_message_length = message_length; + } + + if ((key = EVP_PKEY_new()) == NULL + || RSA_up_ref(o->key.rsa) < 1 + || EVP_PKEY_set1_RSA(key, o->key.rsa) != 1) { + fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + if ((pctx = EVP_PKEY_CTX_new(key, NULL)) == NULL + || EVP_PKEY_verify_init(pctx) != 1 + || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1 + || EVP_PKEY_CTX_set_signature_md(pctx, md) != 1 + || EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, mech->salt) != 1 + || EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf_md) != 1) { + fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } + + rv = EVP_PKEY_verify(pctx, sign, sign_length, my_message, my_message_length); + if (rv == 1) { + debug_print(" [ OK %s ] Signature is valid.", o->id_str); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + } else { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + goto out; + } +out: + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(key); + return rv; +} + +int pss_verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_RSA_PKCS_PSS_PARAMS pss_params; + static int verify_support = 1; + + if (!verify_support) + goto openssl_verify; + + fill_pss_params(&pss_params, mech, o); + sign_mechanism.pParameter = &pss_params; + sign_mechanism.ulParameterLen = sizeof(pss_params); + + /* try C_Verify() if it is supported */ + rv = fp->C_VerifyInit(info->session_handle, &sign_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_VerifyInit: rv = 0x%.8lX", rv); + verify_support = 0; /* avoid trying over and over again */ + goto openssl_verify; + } + + rv = fp->C_Verify(info->session_handle, + message, message_length, sign, sign_length); + + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_SIGN; + debug_print(" [ OK %s ] Verification successful", o->id_str); + return 1; + } + debug_print(" C_Verify: rv = 0x%.8lX", rv); + verify_support = 0; /* avoid trying over and over again */ + +openssl_verify: + debug_print(" [ KEY %s ] Falling back to openssl verification", o->id_str); + return pss_verify_message_openssl(o, info, message, message_length, mech, + sign, sign_length); +} + +/* Perform signature and verification of a message using private key referenced + * in the o object with mechanism defined by mech. + * + * Returns + * * 1 for successful Sign&Verify sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int pss_sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech) +{ + CK_BYTE *message = NULL; + size_t message_length = global_message_length; + CK_BYTE *sign = NULL; + CK_ULONG sign_length = 0; + int rv = 0; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PK_RSA) { + debug_print(" [SKIP %s ] Skip non-RSA key", o->id_str); + return 0; + } + + if (!is_pss_mechanism(mech->mech)) { + mech->usage_flags &= ~CKF_SIGN; + debug_print(" [SKIP %s ] non RSA-PSS mechanism %s", o->id_str, + get_mechanism_name(mech->mech)); + return 0; + } + + if (mech->mech == CKM_RSA_PKCS_PSS) { + message = hash_message(global_message, global_message_length, + mech->hash); + message_length = get_hash_length(mech->hash); + } else { + message = (unsigned char *) SHORT_MESSAGE_TO_SIGN; + } + + debug_print(" [ KEY %s ] Signing message using CKM_%s, CKM_%s," + " CKG_%s, salt_len=%d", o->id_str, + get_mechanism_name(mech->mech), get_mechanism_name(mech->hash), + get_mgf_name(mech->mgf), mech->salt); + rv = pss_sign_message(o, info, message, message_length, mech, &sign); + if (rv <= 0) { + return rv; + } + sign_length = (unsigned long) rv; + + debug_print(" [ KEY %s ] Verify message signature", o->id_str); + rv = pss_verify_message(o, info, message, message_length, mech, + sign, sign_length); + free(sign); + return rv; +} + +/* ignore the prefilled mechanisms and list all combinations of mechanisms + * found, all resonable hash functions, MGFs and salt lengths + */ +void fill_object_pss_mechanisms(token_info_t *info, test_cert_t *o) +{ + const CK_MECHANISM_TYPE *h; + const CK_RSA_PKCS_MGF_TYPE *mgf; + int n = 0, s; + unsigned int j; + + for (j = 0; j < token.num_rsa_mechs; j++) { + test_mech_t *source_mech = &token.rsa_mechs[j]; + + /* skip non-RSA-PSS mechs early */ + if (!is_pss_mechanism(source_mech->mech) && + source_mech->mech != CKM_RSA_PKCS_OAEP) { + continue; + } + + h = get_mechanism_hashes(source_mech->mech); + for (; *h != (CK_MECHANISM_TYPE) -1; h++) { + mgf = get_mgfs(); + for (; *mgf != (CK_RSA_PKCS_MGF_TYPE) -1; mgf++) { + /* OAEP does not have salt */ + if (source_mech->mech == CKM_RSA_PKCS_OAEP) + s = 0; + else + s = -2; + + for (; s <= 0; s++) { + test_mech_t *mech = &o->mechs[n++]; + mech->mech = source_mech->mech; + mech->hash = *h; + mech->mgf = *mgf; + mech->salt = s; + mech->usage_flags = + source_mech->usage_flags; + mech->result_flags = 0; + if (n >= MAX_MECHS) + P11TEST_FAIL(info, + "Too many mechanisms (%d)", + MAX_MECHS); + } + } + } + + } + o->num_mechs = n; +} + +int have_pss_oaep_mechanisms() +{ + unsigned have = 0, i; + for (i = 0; i <= token.num_rsa_mechs; i++) { + if (is_pss_mechanism(token.rsa_mechs[i].mech) || + token.rsa_mechs[i].mech == CKM_RSA_PKCS_OAEP) { + have++; + } + } + return have; +} + +void pss_oaep_test(void **state) { + + token_info_t *info = (token_info_t *) *state; + unsigned int i; + int used, j; + test_certs_t objects; + + P11TEST_START(info); + + if (have_pss_oaep_mechanisms() == 0) { + fprintf(stderr, "Token does not support any RSA-PSS or OAEP mechanisms. Skipping.\n"); + skip(); + } + + objects.count = 0; + objects.data = NULL; + search_for_all_objects(&objects, info); + + debug_print("\nCheck functionality of Sign&Verify and/or Encrypt&Decrypt"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + /* do the Sign&Verify and/or Encrypt&Decrypt */ + used = 0; + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", + o->id_str); + continue; + } + fill_object_pss_mechanisms(info, o); + for (j = 0; j < o->num_mechs; j++) + if (o->mechs[j].mech != CKM_RSA_PKCS_OAEP) + used |= pss_sign_verify_test(o, info, + &(o->mechs[j])); + + for (j = 0; j < o->num_mechs; j++) + if (o->mechs[j].mech == CKM_RSA_PKCS_OAEP) + used |= oaep_encrypt_decrypt_test(o, info, + &(o->mechs[j])); + + if (!used) { + debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX", + o->id_str, o->key_type); + } + } + + if (objects.count == 0) { + printf(" [WARN] No objects to display\n"); + return; + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT]\n"); + printf("[ MECHANISM ] [ HASH ] [ MGF ] [SALT] [ WORKS ] [ WORKS ]\n"); + P11TEST_DATA_ROW(info, 7, + 's', "KEY ID", + 's', "MECHANISM", + 's', "HASH", + 's', "MGF", + 's', "SALT", + 's', "SIGN&VERIFY WORKS", + 's', "ENCRYPT&DECRYPT WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + + /* Do not list non-RSA keys here */ + if (o->type != EVP_PK_RSA) + continue; + + printf("\n[%-6s] [%s]\n", + o->id_str, + o->label); + printf("[ %s ] [%6lu] [ %s ] [%s%s] [%s%s]\n", + o->key_type == CKK_RSA ? "RSA " : + o->key_type == CKK_EC ? " EC " : " ?? ", + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->encrypt ? "[./] " : "[ ] ", + o->decrypt ? " [./] " : " [ ] "); + if (!o->sign && !o->verify && !o->encrypt && !o->decrypt) { + printf(" no usable attributes found ... ignored\n"); + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + printf(" [ %-20s ] [%-6s] [%-11s] [%4d] [ %s ] [ %s ]\n", + get_mechanism_name(mech->mech), + get_mechanism_name(mech->hash), + get_mgf_name(mech->mgf), + mech->salt, + mech->result_flags & FLAGS_SIGN_ANY + ? "[./]" : " ", + mech->result_flags & FLAGS_DECRYPT_ANY + ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 && + (mech->result_flags & FLAGS_DECRYPT_ANY) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 7, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', get_mechanism_name(mech->hash), + 's', get_mgf_name(mech->mgf), + 'd', mech->salt, + 's', mech->result_flags & FLAGS_SIGN_ANY + ? "YES" : "", + 's', mech->result_flags & FLAGS_DECRYPT_ANY + ? "YES" : ""); + } + } + printf(" Public == Cert ----------^ ^ ^ ^ ^ ^ ^\n"); + printf(" Sign Attribute -------------------------------------------' | | | | |\n"); + printf(" Sign&Verify functionality -----------------------------------' | | | |\n"); + printf(" Verify Attribute -----------------------------------------------' | | |\n"); + printf(" Encrypt Attribute ------------------------------------------------------' | |\n"); + printf(" Encrypt & Decrypt functionality -------------------------------------------' |\n"); + printf(" Decrypt Attribute ------------------------------------------------------------'\n"); + + clean_all_objects(&objects); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_pss_oaep.h b/src/tests/p11test/p11test_case_pss_oaep.h new file mode 100644 index 00000000..ce218535 --- /dev/null +++ b/src/tests/p11test/p11test_case_pss_oaep.h @@ -0,0 +1,24 @@ +/* + * p11test_case_pss_oaep.h: RSA-PSS and RSA-OAEP tests + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" + +void pss_oaep_test(void **state); diff --git a/src/tests/p11test/p11test_case_readonly.c b/src/tests/p11test/p11test_case_readonly.c new file mode 100644 index 00000000..9815bea5 --- /dev/null +++ b/src/tests/p11test/p11test_case_readonly.c @@ -0,0 +1,699 @@ +/* + * p11test_case_readonly.c: Sign & Verify tests + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_readonly.h" + +#include +#include +#include + +#define SHORT_MESSAGE_TO_SIGN "Simple message for signing & verifying. It needs to be little bit longer to fit also longer keys and allow the truncation.\n" +#define SHORT_MESSAGE_DIGEST "\x30\x21\x30\x09\x06\x05\x2b\x0e" \ + "\x03\x02\x1a\x05\x00\x04\x14\xd9" \ + "\xdd\xa3\x76\x44\x2f\x50\xe1\xec" \ + "\xd3\x8b\xcd\x6f\xc6\xce\x4e\xfd" \ + "\xd3\x1a\x3f" +#define BUFFER_SIZE 4096 + +const unsigned char *const_message = (unsigned char *) SHORT_MESSAGE_TO_SIGN; + +static unsigned char * +rsa_x_509_pad_message(const unsigned char *message, + unsigned long *message_length, test_cert_t *o, int encrypt) +{ + int pad_message_length = (o->bits+7)/8; + unsigned char *pad_message = malloc(pad_message_length); + if (!encrypt) + RSA_padding_add_PKCS1_type_1(pad_message, pad_message_length, + message, *message_length); + else + RSA_padding_add_PKCS1_type_2(pad_message, pad_message_length, + message, *message_length); + *message_length = pad_message_length; + return pad_message; +} + +int encrypt_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + int rv, padding; + + *enc_message = malloc(RSA_size(o->key.rsa)); + if (*enc_message == NULL) { + debug_print("malloc returned null"); + return -1; + } + + /* Prepare padding for RSA_X_509 */ + padding = ((mech->mech == CKM_RSA_X_509) ? RSA_NO_PADDING : RSA_PKCS1_PADDING); + rv = RSA_public_encrypt(message_length, message, + *enc_message, o->key.rsa, padding); + if (rv < 0) { + free(*enc_message); + debug_print("RSA_public_encrypt: rv = 0x%.8X\n", rv); + return -1; + } + return rv; +} + +int encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM enc_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_ULONG enc_message_length; + static int encrypt_support = 1; + + if (!encrypt_support) + goto openssl_encrypt; + + rv = fp->C_EncryptInit(info->session_handle, &enc_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_EncryptInit: rv = 0x%.8lX", rv); + encrypt_support = 0; /* avoid trying over and over again */ + goto openssl_encrypt; + } + + /* get the expected length */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + NULL, &enc_message_length); + if (rv != CKR_OK) { + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + goto openssl_encrypt; + } + *enc_message = malloc(enc_message_length); + if (*enc_message == NULL) { + debug_print("malloc returned null"); + return -1; + } + + /* Do the actual encryption with allocated buffer */ + rv = fp->C_Encrypt(info->session_handle, message, message_length, + *enc_message, &enc_message_length); + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_SIGN; + return enc_message_length; + } + debug_print(" C_Encrypt: rv = 0x%.8lX", rv); + +openssl_encrypt: + debug_print(" [ KEY %s ] Falling back to openssl encryption", o->id_str); + return encrypt_message_openssl(o, info, message, message_length, mech, + enc_message); +} + +int decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message, + CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM dec_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_ULONG dec_message_length = BUFFER_SIZE; + + rv = fp->C_DecryptInit(info->session_handle, &dec_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to decrypt with this key?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print("C_DecryptInit: rv = 0x%.8lX\n", rv); + return -1; + } + + *dec_message = malloc(dec_message_length); + + always_authenticate(o, info); + + rv = fp->C_Decrypt(info->session_handle, enc_message, + enc_message_length, *dec_message, &dec_message_length); + if (rv != CKR_OK) { + free(*dec_message); + debug_print(" C_Decrypt: rv = 0x%.8lX\n", rv); + return -1; + } + return (int) dec_message_length; +} + +/* Perform encryption and decryption of a message using private key referenced + * in the o object with mechanism defined by mech. + * + * NONE of the reasonable mechanisms support multipart encryption/decryption + * + * Returns + * * 1 for successful Encrypt&Decrypt sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart) +{ + CK_BYTE *message = NULL; + CK_BYTE *dec_message = NULL; + int dec_message_length = 0; + unsigned char *enc_message; + int enc_message_length, rv; + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PK_RSA) { + debug_print(" [ KEY %s ] Skip non-RSA key for encryption", o->id_str); + return 0; + } + + if (mech->mech == CKM_RSA_PKCS_OAEP) { + mech->usage_flags &= ~CKF_DECRYPT; + debug_print(" [SKIP %s ] RSA-OAEP tested separately", o->id_str); + return 0; + } + + if (mech->mech != CKM_RSA_X_509 && mech->mech != CKM_RSA_PKCS) { + debug_print(" [ KEY %s ] Skip encryption for non-supported mechanism %s", + o->id_str, get_mechanism_name(mech->mech)); + return 0; + } + + if (mech->mech == CKM_RSA_X_509) + message = rsa_x_509_pad_message(const_message, + &message_length, o, 1); + else + message = (CK_BYTE *) strdup(SHORT_MESSAGE_TO_SIGN); + + debug_print(" [ KEY %s ] Encrypt message using CKM_%s", + o->id_str, get_mechanism_name(mech->mech)); + enc_message_length = encrypt_message(o, info, message, message_length, + mech, &enc_message); + if (enc_message_length <= 0) { + free(message); + return -1; + } + + debug_print(" [ KEY %s ] Decrypt message", o->id_str); + dec_message_length = decrypt_message(o, info, enc_message, + enc_message_length, mech, &dec_message); + free(enc_message); + if (dec_message_length <= 0) { + free(message); + return -1; + } + + if (memcmp(dec_message, message, dec_message_length) == 0 + && (unsigned int) dec_message_length == message_length) { + debug_print(" [ OK %s ] Text decrypted successfully.", o->id_str); + mech->result_flags |= FLAGS_DECRYPT; + rv = 1; + } else { + dec_message[dec_message_length] = '\0'; + debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s", + o->id_str, dec_message); + rv = 0; + } + free(dec_message); + free(message); + return rv; +} + +int sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char **sign, + int multipart) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 }; + CK_ULONG sign_length = 0; + char *name; + + rv = fp->C_SignInit(info->session_handle, &sign_mechanism, + o->private_handle); + if (rv == CKR_KEY_TYPE_INCONSISTENT) { + debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str); + return 0; + } else if (rv == CKR_MECHANISM_INVALID) { + debug_print(" [SKIP %s ] Bad mechanism. Not supported?", o->id_str); + return 0; + } else if (rv != CKR_OK) { + debug_print(" C_SignInit: rv = 0x%.8lX\n", rv); + return -1; + } + + always_authenticate(o, info); + + if (multipart) { + int part = message_length / 3; + rv = fp->C_SignUpdate(info->session_handle, message, part); + if (rv == CKR_MECHANISM_INVALID) { + fprintf(stderr, " Multipart Signature not supported with CKM_%s\n", + get_mechanism_name(mech->mech)); + return -1; + } else if (rv != CKR_OK) { + fprintf(stderr, " C_SignUpdate: rv = 0x%.8lX\n", rv); + return -1; + } + rv = fp->C_SignUpdate(info->session_handle, message + part, message_length - part); + if (rv != CKR_OK) { + fprintf(stderr, " C_SignUpdate: rv = 0x%.8lX\n", rv); + return -1; + } + /* Call C_SignFinal with NULL argument to find out the real size of signature */ + rv = fp->C_SignFinal(info->session_handle, *sign, &sign_length); + if (rv != CKR_OK) { + fprintf(stderr, " C_SignFinal: rv = 0x%.8lX\n", rv); + return -1; + } + + *sign = malloc(sign_length); + if (*sign == NULL) { + fprintf(stderr, "%s: malloc failed", __func__); + return -1; + } + + /* Call C_SignFinal with allocated buffer to the actual signature */ + rv = fp->C_SignFinal(info->session_handle, *sign, &sign_length); + name = "C_SignFinal"; + } else { + /* Call C_Sign with NULL argument to find out the real size of signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + if (rv != CKR_OK) { + fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv); + return -1; + } + + *sign = malloc(sign_length); + if (*sign == NULL) { + fprintf(stderr, "%s: malloc failed", __func__); + return -1; + } + + /* Call C_Sign with allocated buffer to the actual signature */ + rv = fp->C_Sign(info->session_handle, + message, message_length, *sign, &sign_length); + name = "C_Sign"; + } + if (rv != CKR_OK) { + free(*sign); + fprintf(stderr, " %s: rv = 0x%.8lX\n", name, rv); + return -1; + } + return sign_length; +} + +int verify_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length) +{ + CK_RV rv; + CK_BYTE *cmp_message = NULL; + int cmp_message_length; + + if (o->type == EVP_PK_RSA) { + int type; + + /* raw RSA mechanism */ + if (mech->mech == CKM_RSA_PKCS || mech->mech == CKM_RSA_X_509) { + CK_BYTE dec_message[BUFFER_SIZE]; + int padding = ((mech->mech == CKM_RSA_X_509) + ? RSA_NO_PADDING : RSA_PKCS1_PADDING); + int dec_message_length = RSA_public_decrypt(sign_length, sign, + dec_message, o->key.rsa, padding); + if (dec_message_length < 0) { + fprintf(stderr, "RSA_public_decrypt: rv = %d: %s\n", dec_message_length, + ERR_error_string(ERR_peek_last_error(), NULL)); + return -1; + } + if (memcmp(dec_message, message, dec_message_length) == 0 + && dec_message_length == (int) message_length) { + debug_print(" [ OK %s ] Signature is valid.", o->id_str); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + return 1; + } else { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + return 0; + } + } + + /* Digest mechanisms */ + switch (mech->mech) { + case CKM_SHA1_RSA_PKCS: + cmp_message = SHA1(message, message_length, NULL); + cmp_message_length = SHA_DIGEST_LENGTH; + type = NID_sha1; + break; + case CKM_SHA224_RSA_PKCS: + cmp_message = SHA224(message, message_length, NULL); + cmp_message_length = SHA224_DIGEST_LENGTH; + type = NID_sha224; + break; + case CKM_SHA256_RSA_PKCS: + cmp_message = SHA256(message, message_length, NULL); + cmp_message_length = SHA256_DIGEST_LENGTH; + type = NID_sha256; + break; + case CKM_SHA384_RSA_PKCS: + cmp_message = SHA384(message, message_length, NULL); + cmp_message_length = SHA384_DIGEST_LENGTH; + type = NID_sha384; + break; + case CKM_SHA512_RSA_PKCS: + cmp_message = SHA512(message, message_length, NULL); + cmp_message_length = SHA512_DIGEST_LENGTH; + type = NID_sha512; + break; + case CKM_MD5_RSA_PKCS: + cmp_message = MD5(message, message_length, NULL); + cmp_message_length = MD5_DIGEST_LENGTH; + type = NID_md5; + break; + case CKM_RIPEMD160_RSA_PKCS: + cmp_message = RIPEMD160(message, message_length, NULL); + cmp_message_length = RIPEMD160_DIGEST_LENGTH; + type = NID_ripemd160; + break; + default: + debug_print(" [SKIP %s ] Skip verify of unknown mechanism", o->id_str); + return 0; + } + rv = RSA_verify(type, cmp_message, cmp_message_length, + sign, sign_length, o->key.rsa); + if (rv == 1) { + debug_print(" [ OK %s ] Signature is valid.", o->id_str); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + } else { + fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n", + o->id_str, ERR_error_string(ERR_peek_last_error(), NULL)); + return -1; + } + } else if (o->type == EVP_PK_EC) { + unsigned int nlen; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BIGNUM *r = NULL, *s = NULL; + if (sig == NULL) { + fprintf(stderr, "ECDSA_SIG_new: failed"); + return -1; + } + nlen = sign_length/2; + r = BN_bin2bn(&sign[0], nlen, NULL); + s = BN_bin2bn(&sign[nlen], nlen, NULL); + ECDSA_SIG_set0(sig, r, s); + switch (mech->mech) { + case CKM_ECDSA_SHA512: + cmp_message = SHA512(message, message_length, NULL); + cmp_message_length = SHA512_DIGEST_LENGTH; + break; + case CKM_ECDSA_SHA384: + cmp_message = SHA384(message, message_length, NULL); + cmp_message_length = SHA384_DIGEST_LENGTH; + break; + case CKM_ECDSA_SHA256: + cmp_message = SHA256(message, message_length, NULL); + cmp_message_length = SHA256_DIGEST_LENGTH; + break; + case CKM_ECDSA_SHA1: + cmp_message = SHA1(message, message_length, NULL); + cmp_message_length = SHA_DIGEST_LENGTH; + break; + case CKM_ECDSA: + cmp_message = message; + cmp_message_length = message_length; + break; + default: + debug_print(" [SKIP %s ] Skip verify of unknown mechanism", o->id_str); + return 0; + } + rv = ECDSA_do_verify(cmp_message, cmp_message_length, sig, o->key.ec); + if (rv == 1) { + ECDSA_SIG_free(sig); + debug_print(" [ OK %s ] EC Signature of length %lu is valid.", + o->id_str, message_length); + mech->result_flags |= FLAGS_SIGN_OPENSSL; + return 1; + } else { + ECDSA_SIG_free(sig); + fprintf(stderr, " [FAIL %s ] ECDSA_do_verify: rv = %lu: %s\n", o->id_str, + rv, ERR_error_string(ERR_peek_last_error(), NULL)); + return -1; + } + } else { + fprintf(stderr, " [ KEY %s ] Unknown type. Not verifying", o->id_str); + } + return 0; +} + +int verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message, + CK_ULONG message_length, test_mech_t *mech, unsigned char *sign, + CK_ULONG sign_length, int multipart) +{ + CK_RV rv; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 }; + static int verify_support = 1; +#ifndef NDEBUG + char *name; +#endif + + if (!verify_support) + goto openssl_verify; + + /* try C_Verify() if it is supported */ + rv = fp->C_VerifyInit(info->session_handle, &sign_mechanism, + o->public_handle); + if (rv != CKR_OK) { + debug_print(" C_VerifyInit: rv = 0x%.8lX", rv); + verify_support = 0; /* avoid trying over and over again */ + goto openssl_verify; + } + if (multipart) { + int part = message_length / 3; + /* First part */ + rv = fp->C_VerifyUpdate(info->session_handle, message, part); + if (rv != CKR_OK) { + debug_print(" C_VerifyUpdate: rv = 0x%.8lX", rv); + goto openssl_verify; + } + /* Second part */ + rv = fp->C_VerifyUpdate(info->session_handle, message + part, + message_length - part); + if (rv != CKR_OK) { + debug_print(" C_VerifyUpdate: rv = 0x%.8lX", rv); + goto openssl_verify; + } + /* Final */ + rv = fp->C_VerifyFinal(info->session_handle, + sign, sign_length); +#ifndef NDEBUG + name = "C_VerifyFinal"; +#endif + } else { + rv = fp->C_Verify(info->session_handle, + message, message_length, sign, sign_length); +#ifndef NDEBUG + name = "C_Verify"; +#endif + } + if (rv == CKR_OK) { + mech->result_flags |= FLAGS_SIGN; + debug_print(" [ OK %s ] Verification successful", o->id_str); + return 1; + } + debug_print(" %s: rv = 0x%.8lX", name, rv); + verify_support = 0; /* avoid trying over and over again */ + +openssl_verify: + debug_print(" [ KEY %s ] Falling back to openssl verification", o->id_str); + return verify_message_openssl(o, info, message, message_length, mech, + sign, sign_length); +} + +/* Perform signature and verification of a message using private key referenced + * in the o object with mechanism defined by mech. Message length can be + * specified using argument message_length. + * + * Returns + * * 1 for successful Sign&Verify sequence + * * 0 for skipped test (unsupported mechanism, key, ...) + * * -1 otherwise. + * Serious errors terminate the execution. + */ +int sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart) +{ + CK_BYTE *message = NULL; + CK_BYTE *sign = NULL; + CK_ULONG sign_length = 0; + int rv = 0; + + if (message_length > strlen(SHORT_MESSAGE_TO_SIGN)) + fail_msg("Truncate is longer than the actual message"); + + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", o->id_str); + return 0; + } + + if (o->type != EVP_PK_EC && o->type != EVP_PK_RSA) { + debug_print(" [SKIP %s ] Skip non-RSA and non-EC key", o->id_str); + return 0; + } + + if (is_pss_mechanism(mech->mech)) { + mech->usage_flags &= ~CKF_SIGN; + debug_print(" [SKIP %s ] RSA-PSS tested separately", o->id_str); + return 0; + } + + if (mech->mech == CKM_RSA_X_509) /* manually add padding */ + message = rsa_x_509_pad_message(const_message, + &message_length, o, 0); + else if (mech->mech == CKM_RSA_PKCS) { + /* DigestInfo + SHA1(message) */ + message_length = 35; + message = malloc(message_length * sizeof(unsigned char)); + memcpy(message, SHORT_MESSAGE_DIGEST, message_length); + } else + message = (CK_BYTE *) strdup(SHORT_MESSAGE_TO_SIGN); + + debug_print(" [ KEY %s ] Signing message of length %lu using CKM_%s", + o->id_str, message_length, get_mechanism_name(mech->mech)); + rv = sign_message(o, info, message, message_length, mech, &sign, multipart); + if (rv <= 0) { + free(message); + return rv; + } + sign_length = (unsigned long) rv; + + debug_print(" [ KEY %s ] Verify message signature", o->id_str); + rv = verify_message(o, info, message, message_length, mech, + sign, sign_length, multipart); + free(sign); + free(message); + return rv; +} + +void readonly_tests(void **state) { + + token_info_t *info = (token_info_t *) *state; + unsigned int i; + int used, j; + test_certs_t objects; + + objects.count = 0; + objects.data = NULL; + + search_for_all_objects(&objects, info); + + P11TEST_START(info); + debug_print("\nCheck functionality of Sign&Verify and/or Encrypt&Decrypt"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + /* do the Sign&Verify and/or Encrypt&Decrypt */ + used = 0; + if (o->private_handle == CK_INVALID_HANDLE) { + debug_print(" [SKIP %s ] Missing private key", + o->id_str); + continue; + } + /* XXX some keys do not have appropriate flags, but we can use them + * or vice versa */ + //if (o->sign && o->verify) + for (j = 0; j < o->num_mechs; j++) + used |= sign_verify_test(&(objects.data[i]), info, + &(o->mechs[j]), 32, 0); + + //if (o->encrypt && o->decrypt) + for (j = 0; j < o->num_mechs; j++) + used |= encrypt_decrypt_test(&(objects.data[i]), info, + &(o->mechs[j]), 32, 0); + + if (!used) { + debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX", + o->id_str, o->key_type); + } + } + + if (objects.count == 0) { + printf(" [WARN] No objects to display\n"); + return; + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT] [WRAP&UNWR] [ DERIVE ]\n"); + P11TEST_DATA_ROW(info, 4, + 's', "KEY ID", + 's', "MECHANISM", + 's', "SIGN&VERIFY WORKS", + 's', "ENCRYPT&DECRYPT WORKS"); + for (i = 0; i < objects.count; i++) { + test_cert_t *o = &objects.data[i]; + printf("\n[%-6s] [%s]\n", + o->id_str, + o->label); + printf("[ %s ] [%6lu] [ %s ] [%s%s] [%s%s] [%s %s] [%s%s]\n", + o->key_type == CKK_RSA ? "RSA " : + o->key_type == CKK_EC ? " EC " : " ?? ", + o->bits, + o->verify_public == 1 ? " ./ " : " ", + o->sign ? "[./] " : "[ ] ", + o->verify ? " [./] " : " [ ] ", + o->encrypt ? "[./] " : "[ ] ", + o->decrypt ? " [./] " : " [ ] ", + o->wrap ? "[./]" : "[ ]", + o->unwrap ? "[./]" : "[ ]", + o->derive_pub ? "[./]" : "[ ]", + o->derive_priv ? "[./]" : "[ ]"); + if (!o->sign && !o->verify && !o->encrypt && !o->decrypt) { + printf(" no usable attributes found ... ignored\n"); + continue; + } + for (j = 0; j < o->num_mechs; j++) { + test_mech_t *mech = &o->mechs[j]; + if ((mech->usage_flags & CKF_SIGN) == 0) { + /* not applicable mechanisms are skipped */ + continue; + } + printf(" [ %-20s ] [ %s ] [ %s ] [ ] [ ]\n", + get_mechanism_name(mech->mech), + mech->result_flags & FLAGS_SIGN_ANY ? "[./]" : " ", + mech->result_flags & FLAGS_DECRYPT_ANY ? "[./]" : " "); + if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 && + (mech->result_flags & FLAGS_DECRYPT_ANY) == 0) + continue; /* skip empty rows for export */ + P11TEST_DATA_ROW(info, 4, + 's', o->id_str, + 's', get_mechanism_name(mech->mech), + 's', mech->result_flags & FLAGS_SIGN_ANY ? "YES" : "", + 's', mech->result_flags & FLAGS_DECRYPT_ANY ? "YES" : ""); + } + } + printf(" Public == Cert -----^ ^ ^ ^ ^ ^ ^ ^----^- Attributes\n"); + printf(" Sign Attribute -------------' | | | | '---- Decrypt Attribute\n"); + printf(" Sign&Verify functionality -----' | | '------- Enc&Dec functionality\n"); + printf(" Verify Attribute -----------------' '---------- Encrypt Attribute\n"); + + clean_all_objects(&objects); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_readonly.h b/src/tests/p11test/p11test_case_readonly.h new file mode 100644 index 00000000..8f8a7576 --- /dev/null +++ b/src/tests/p11test/p11test_case_readonly.h @@ -0,0 +1,29 @@ +/* + * p11test_case_readonly.h: Sign & Verify tests + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" + +void readonly_tests(void **state); +int encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart); +int sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech, + CK_ULONG message_length, int multipart); + diff --git a/src/tests/p11test/p11test_case_usage.c b/src/tests/p11test/p11test_case_usage.c new file mode 100644 index 00000000..6973fd29 --- /dev/null +++ b/src/tests/p11test/p11test_case_usage.c @@ -0,0 +1,137 @@ +/* + * p11test_case_usage.c: Check if the usage flags are sane + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "p11test_case_usage.h" + +void usage_test(void **state) { + unsigned int i; + int errors = 0; + token_info_t *info = (token_info_t *) *state; + + test_certs_t objects; + objects.count = 0; + objects.data = NULL; + + P11TEST_START(info); + search_for_all_objects(&objects, info); + + debug_print("Check if the usage flags are sane.\n"); + for (i = 0; i < objects.count; i++) { + /* Ignore if there is missing private key */ + if (objects.data[i].private_handle == CK_INVALID_HANDLE) + continue; + + /* The usage flags are paired */ + if (objects.data[i].sign && !objects.data[i].verify) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Sign is set, Verify should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].decrypt && !objects.data[i].encrypt) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Decrypt is set, Encrypt should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].unwrap && !objects.data[i].wrap) { + errors++; + fprintf(stderr, " [ ERROR %s ] If Unwrap is set, Wrap should be set too.\n", + objects.data[i].id_str); + } + if (objects.data[i].derive_pub != objects.data[i].derive_priv) { + errors++; + fprintf(stderr, " [ ERROR %s ] Derive should be set on both private and public part.\n", + objects.data[i].id_str); + } + + /* We have at least one usage flag for every key group */ + if (! objects.data[i].sign && ! objects.data[i].verify && + ! objects.data[i].encrypt && ! objects.data[i].decrypt && + ! objects.data[i].wrap && ! objects.data[i].unwrap && + ! objects.data[i].derive_pub && ! objects.data[i].derive_priv) { + errors++; + fprintf(stderr, " [ ERROR %s ] Key group should have at least one usage flag.\n", + objects.data[i].id_str); + } + } + + /* print summary */ + printf("[KEY ID] [LABEL]\n"); + printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT] [WRAP&UNWR] [ DERIVE ] [ALWAYS_AUTH]\n"); + P11TEST_DATA_ROW(info, 14, + 's', "KEY ID", + 's', "LABEL", + 's', "TYPE", + 's', "BITS", + 's', "VERIFY PUBKEY", + 's', "SIGN", + 's', "VERIFY", + 's', "ENCRYPT", + 's', "DECRYPT", + 's', "WRAP", + 's', "UNWRAP", + 's', "DERIVE PUBLIC", + 's', "DERIVE PRIVATE", + 's', "ALWAYS AUTH"); + for (i = 0; i < objects.count; i++) { + printf("\n[%-6s] [%s]\n", + objects.data[i].id_str, + objects.data[i].label); + printf("[ %s ] [%6lu] [ %s ] [%s%s] [%s%s] [%s %s] [%s%s] [ %s ]\n", + objects.data[i].key_type == CKK_RSA ? "RSA " : + objects.data[i].key_type == CKK_EC ? " EC " : " ?? ", + objects.data[i].bits, + objects.data[i].verify_public == 1 ? " ./ " : " ", + objects.data[i].sign ? "[./] " : "[ ] ", + objects.data[i].verify ? " [./] " : " [ ] ", + objects.data[i].encrypt ? "[./] " : "[ ] ", + objects.data[i].decrypt ? " [./] " : " [ ] ", + objects.data[i].wrap ? "[./]" : "[ ]", + objects.data[i].unwrap ? "[./]" : "[ ]", + objects.data[i].derive_pub ? "[./]" : "[ ]", + objects.data[i].derive_priv ? "[./]" : "[ ]", + objects.data[i].always_auth ? "[./]" : "[ ]"); + P11TEST_DATA_ROW(info, 14, + 's', objects.data[i].id_str, + 's', objects.data[i].label, + 's', objects.data[i].key_type == CKK_RSA ? "RSA" : + objects.data[i].key_type == CKK_EC ? "EC" : "??", + 'd', objects.data[i].bits, + 's', objects.data[i].verify_public == 1 ? "YES" : "", + 's', objects.data[i].sign ? "YES" : "", + 's', objects.data[i].verify ? "YES" : "", + 's', objects.data[i].encrypt ? "YES" : "", + 's', objects.data[i].decrypt ? "YES" : "", + 's', objects.data[i].wrap ? "YES" : "", + 's', objects.data[i].unwrap ? "YES" : "", + 's', objects.data[i].derive_pub ? "YES" : "", + 's', objects.data[i].derive_priv ? "YES" : "", + 's', objects.data[i].always_auth ? "YES" : ""); + } + printf(" Public == Cert -----^ ^-----^ ^-----^ ^----^ ^---^\n"); + printf(" Sign & Verify Attributes ------' | | |\n"); + printf(" Encrypt & Decrypt Attributes ----------------' | |\n"); + printf(" Wrap & Unwrap Attributes ---------------------------------' |\n"); + printf(" Public and Private key Derive Attributes -----------------------------'\n"); + + clean_all_objects(&objects); + if (errors > 0) + P11TEST_FAIL(info, "Not all the usage flags were successfully verified. See the verbose log."); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_usage.h b/src/tests/p11test/p11test_case_usage.h new file mode 100644 index 00000000..a728890d --- /dev/null +++ b/src/tests/p11test/p11test_case_usage.h @@ -0,0 +1,25 @@ +/* + * p11test_case_usage.h: Check if the usage flags are sane + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_case_common.h" +#include "p11test_case_readonly.h" + +void usage_test(void **state); diff --git a/src/tests/p11test/p11test_case_wait.c b/src/tests/p11test/p11test_case_wait.c new file mode 100644 index 00000000..2d88c3b0 --- /dev/null +++ b/src/tests/p11test/p11test_case_wait.c @@ -0,0 +1,59 @@ +/* + * p11test_case_wait.c: Test slot events (insert / remove) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "p11test_case_wait.h" + +void wait_test(void **state) { + + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR fp = info->function_pointer; + CK_RV rv; + CK_SLOT_ID slot_id; + CK_SLOT_INFO slot_info; + int token_present = 0; + + P11TEST_START(info); + if (!info->interactive) { + fprintf(stderr, "To test wait, run in interactive mode (-i switch).\n"); + P11TEST_SKIP(info); + } + + do { + printf(" [ Waiting for slot event ... ]\n"); + + rv = fp->C_WaitForSlotEvent(0, &slot_id, NULL_PTR); + if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + fprintf(stderr, "Function does not support call with blocking wait. Skipping.\n"); + skip(); + } else if (rv != CKR_OK) + P11TEST_FAIL(info, "C_WaitForSlotEvent: rv = 0x%.8lX\n", rv); + + rv = fp->C_GetSlotInfo(slot_id, &slot_info); + if (rv != CKR_OK) + P11TEST_FAIL(info, "C_GetSlotInfo: rv = 0x%.8lX\n", rv); + + token_present = ((slot_info.flags & CKF_TOKEN_PRESENT) != 0); + + printf(" [ Slot %lu ] %s\n", slot_id, slot_info.slotDescription); + printf(" Status: %s\n", + token_present ? "Token present": "No token"); + } while (!token_present); + P11TEST_PASS(info); +} diff --git a/src/tests/p11test/p11test_case_wait.h b/src/tests/p11test/p11test_case_wait.h new file mode 100644 index 00000000..36f75e2a --- /dev/null +++ b/src/tests/p11test/p11test_case_wait.h @@ -0,0 +1,24 @@ +/* + * p11test_case_wait.h: Test slot events (insert / remove) + * + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "p11test_case_common.h" + +void wait_test(void **state); + diff --git a/src/tests/p11test/p11test_common.h b/src/tests/p11test/p11test_common.h new file mode 100644 index 00000000..d3ca304d --- /dev/null +++ b/src/tests/p11test/p11test_common.h @@ -0,0 +1,89 @@ +/* + * p11test_common.h: Test suite shared declarations for PKCS#11 API + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#ifndef P11TEST_COMMON_H +#define P11TEST_COMMON_H +#include "config.h" +#include +#include +#include +#include +#include +#include "pkcs11/pkcs11.h" +#include "libopensc/sc-ossl-compat.h" + +#define MAX_MECHS 200 + +#ifndef NDEBUG + #define debug_print(fmt, ...) \ + { fprintf(stderr, fmt "\n", ##__VA_ARGS__); } while (0) +#else + #define debug_print(fmt, ...) +#endif + +#define FLAGS_SIGN 0x01 +#define FLAGS_SIGN_OPENSSL 0x02 +#define FLAGS_SIGN_ANY ( FLAGS_SIGN | FLAGS_SIGN_OPENSSL ) +#define FLAGS_DECRYPT 0x04 +#define FLAGS_DECRYPT_OPENSSL 0x08 +#define FLAGS_DECRYPT_ANY ( FLAGS_DECRYPT | FLAGS_DECRYPT_OPENSSL ) + +typedef struct { + char *outfile; + FILE *fd; + int in_test; + int first; + int in_data; + int first_data; +} log_context_t; + +typedef struct { + CK_MECHANISM_TYPE mech; + CK_MECHANISM_TYPE hash; + CK_RSA_PKCS_MGF_TYPE mgf; + int salt; + int usage_flags; + int result_flags; +} test_mech_t; + +typedef struct { + CK_FUNCTION_LIST_PTR function_pointer; + CK_SLOT_ID slot_id; + CK_SESSION_HANDLE session_handle; + CK_UTF8CHAR* pin; + size_t pin_length; + char *library_path; + unsigned int interactive; + log_context_t log; + + test_mech_t rsa_mechs[MAX_MECHS]; + size_t num_rsa_mechs; + test_mech_t ec_mechs[MAX_MECHS]; + size_t num_ec_mechs; + test_mech_t keygen_mechs[MAX_MECHS]; + size_t num_keygen_mechs; +} token_info_t; + +token_info_t token; + +#endif /* P11TEST_COMMON_H */ + diff --git a/src/tests/p11test/p11test_helpers.c b/src/tests/p11test/p11test_helpers.c new file mode 100644 index 00000000..bdfd40d0 --- /dev/null +++ b/src/tests/p11test/p11test_helpers.c @@ -0,0 +1,207 @@ +/* + * p11test_helpers.c: Test suite for PKCS#11 API: Supporting functions + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_helpers.h" +#include "p11test_loader.h" + +int open_session(token_info_t *info) { + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + CK_RV rv; + + rv = function_pointer->C_OpenSession(info->slot_id, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, + &info->session_handle); + + if(rv != CKR_OK) + return 1; + + debug_print("Session was successfully created"); + return 0; +} + +int initialize_cryptoki(token_info_t *info) { + + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + CK_RV rv; + + rv = function_pointer->C_Initialize(NULL_PTR); + if(rv != CKR_OK){ + fprintf(stderr,"Could not initialize CRYPTOKI!\n"); + return 1; + } + + if(get_slot_with_card(info)) { + function_pointer->C_Finalize(NULL_PTR); + fprintf(stderr,"There is no card present in reader.\n"); + return 1; + } + + return 0; +} + +int token_initialize(void **state) { + token_info_t *info = (token_info_t *) *state; + if(initialize_cryptoki(info)) { + debug_print("CRYPTOKI couldn't be initialized"); + return 1; + } + return 0; +} + +void logfile_init(token_info_t *info) { + if (token.log.outfile == NULL) + return; + + if ((info->log.fd = fopen(token.log.outfile, "w")) == NULL) + fail_msg("Couldn't open file for test results."); + fprintf(info->log.fd, "{\n\"time\": 0,\n\"results\": ["); + info->log.in_test = 0; + info->log.first = 1; +} + +void logfile_finalize(token_info_t *info) { + if (info == NULL || info->log.fd == NULL) + return; + + /* Make sure the JSON object for test is closed */ + if (info->log.in_test) { + fprintf(info->log.fd, ",\n\t\"result\": \"unknown\"\n},"); + info->log.in_test = 0; + } + + fprintf(info->log.fd, "]\n}\n"); + fclose(info->log.fd); +} + +int group_setup(void **state) +{ + + token_info_t * info = calloc(sizeof(token_info_t), 1); + + assert_non_null(info); + + info->pin = token.pin; + info->pin_length = token.pin_length; + info->interactive = token.interactive; + info->slot_id = token.slot_id; + + if (load_pkcs11_module(info, token.library_path)) { + free(info); + fail_msg("Could not load module!\n"); + } + + logfile_init(info); + + *state = info; + return 0; +} + +int group_teardown(void **state) { + + token_info_t *info = (token_info_t *) *state; + debug_print("Clearing state after group tests!"); + // XXX do not finalize already Finalized + //if(info && info->function_pointer) + // info->function_pointer->C_Finalize(NULL_PTR); + + free(token.library_path); + free(token.pin); + + logfile_finalize(info); + free(info); + + close_pkcs11_module(); + + return 0; +} + +int prepare_token(token_info_t *info) { + if(initialize_cryptoki(info)) { + debug_print("CRYPTOKI couldn't be initialized"); + return 1; + } + + if(open_session(info)) { + debug_print("Could not open session to token!"); + return 1; + } + + return 0; +} + +int finalize_token(token_info_t *info) { + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + info->session_handle = 0; + debug_print("Closing all sessions"); + function_pointer->C_CloseAllSessions(info->slot_id); + debug_print("Finalize CRYPTOKI"); + function_pointer->C_Finalize(NULL_PTR); + return 0; +} + +int user_login_setup(void **state) { + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + CK_RV rv; + + if (prepare_token(info)) + fail_msg("Could not prepare token.\n"); + + debug_print("Logging in to the token!"); + rv = function_pointer->C_Login(info->session_handle, CKU_USER, + token.pin, token.pin_length); + + if(rv != CKR_OK) + fail_msg("Could not login to token with user PIN '%s'\n", token.pin); + + return 0; +} + +int after_test_cleanup(void **state) { + + token_info_t *info = (token_info_t *) *state; + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + debug_print("Logging out from token"); + function_pointer->C_Logout(info->session_handle); + + finalize_token(info); + return 0; +} + +int token_setup(void **state) { + token_info_t *info = (token_info_t *) *state; + + if(prepare_token(info)) + fail_msg("Could not prepare token.\n"); + + return 0; +} + +int token_cleanup(void **state) { + token_info_t *info = (token_info_t *) *state; + + finalize_token(info); + return 0; +} + diff --git a/src/tests/p11test/p11test_helpers.h b/src/tests/p11test/p11test_helpers.h new file mode 100644 index 00000000..fac3850d --- /dev/null +++ b/src/tests/p11test/p11test_helpers.h @@ -0,0 +1,37 @@ +/* + * p11test_helpers.h: Test suite for PKCS#11 API: Supporting functions + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#ifndef P11TEST_HELPERS_H +#define P11TEST_HELPERS_H +#include "p11test_common.h" + +int group_setup(void **state); +int group_teardown(void **state); + +int user_login_setup(void **state); +int after_test_cleanup(void **state); + +int token_setup(void **state); +int token_cleanup(void **state); + +int token_initialize(void **state); +#endif //P11TEST_HELPERS_H diff --git a/src/tests/p11test/p11test_loader.c b/src/tests/p11test/p11test_loader.c new file mode 100644 index 00000000..6dc2236c --- /dev/null +++ b/src/tests/p11test/p11test_loader.c @@ -0,0 +1,147 @@ +/* + * p11test_loader.c: Library loader for PKCS#11 test suite + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#include "p11test_loader.h" + +static void *pkcs11_so; + +int get_slot_with_card(token_info_t * info) +{ + CK_SLOT_ID_PTR slot_list; + CK_SLOT_ID slot_id; + CK_ULONG slot_count = 0; + CK_RV rv; + int error = 0; + unsigned int i; + + CK_FUNCTION_LIST_PTR function_pointer = info->function_pointer; + + /* Get slot list for memory allocation */ + rv = function_pointer->C_GetSlotList(0, NULL_PTR, &slot_count); + + if ((rv == CKR_OK) && (slot_count > 0)) { + slot_list = malloc(slot_count * sizeof (CK_SLOT_ID)); + + if (slot_list == NULL) { + fprintf(stderr, "System error: unable to allocate memory\n"); + return 1; + } + + /* Get the slot list for processing */ + rv = function_pointer->C_GetSlotList(0, slot_list, &slot_count); + if (rv != CKR_OK) { + fprintf(stderr, "GetSlotList failed: unable to get slot count.\n"); + error = 1; + goto cleanup; + } + } else { + fprintf(stderr, "GetSlotList failed: unable to get slot list.\n"); + return 1; + } + + /* Find a slot capable of specified mechanism */ + for (i = 0; i < slot_count; i++) { + CK_SLOT_INFO slot_info; + slot_id = slot_list[i]; + + rv = function_pointer->C_GetSlotInfo(slot_id, &slot_info); + if (rv != CKR_OK) + continue; + + if (info->slot_id == slot_id) { + if (info->slot_id == slot_list[i]) { /* explicitly specified slot */ + debug_print("Manually selected slot %lu (%s a token)\n", info->slot_id, + ((slot_info.flags & CKF_TOKEN_PRESENT) ? "with" : "without")); + goto cleanup; + } + } + + if (slot_info.flags & CKF_TOKEN_PRESENT) { + /* first found slot if not specified */ + if (info->slot_id == (unsigned long) -1) { + info->slot_id = slot_id; + goto cleanup; + } + } + } + error = 1; + fprintf(stderr, "No slot with card inserted or the selected slot does not exist\n"); + + cleanup: + if (slot_list) { + free(slot_list); + } + + return error; +} + +int load_pkcs11_module(token_info_t * info, const char* path_to_pkcs11_library) { + CK_RV rv; + CK_RV (*C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR) = 0; + + if(strlen(path_to_pkcs11_library) == 0) { + fprintf(stderr, "You have to specify path to PKCS#11 library."); + return 1; + } + + pkcs11_so = dlopen(path_to_pkcs11_library, RTLD_NOW); + + if (!pkcs11_so) { + fprintf(stderr, "Error loading pkcs#11 so: %s\n", dlerror()); + return 1; + } + + C_GetFunctionList = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(pkcs11_so, "C_GetFunctionList"); + + if (!C_GetFunctionList) { + fprintf(stderr, "Could not get function list: %s\n", dlerror()); + return 1; + } + + rv = C_GetFunctionList(&info->function_pointer); + if (CKR_OK != rv) { + fprintf(stderr, "C_GetFunctionList call failed: 0x%.8lX", rv); + return 1; + } + + rv = info->function_pointer->C_Initialize(NULL_PTR); + + if (rv != CKR_OK) { + fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv); + return 1; + } + + if (get_slot_with_card(info)) { + fprintf(stderr, "There is no card present in reader.\n"); + info->function_pointer->C_Finalize(NULL_PTR); + return 1; + } + + info->function_pointer->C_Finalize(NULL_PTR); + return 0; +} + +void close_pkcs11_module() { + if(pkcs11_so) + dlclose(pkcs11_so); +} + diff --git a/src/tests/p11test/p11test_loader.h b/src/tests/p11test/p11test_loader.h new file mode 100644 index 00000000..5b0c62a3 --- /dev/null +++ b/src/tests/p11test/p11test_loader.h @@ -0,0 +1,34 @@ +/* + * p11test_loader.h: Library loader for PKCS#11 test suite + * + * Copyright (C) 2016 Martin Strhársky + * Copyright (C) 2016, 2017 Red Hat, Inc. + * + * Author: Jakub Jelen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ + +#ifndef P11TEST_LOADER_H +#define P11TEST_LOADER_H + +#include +#include "p11test_helpers.h" + +int load_pkcs11_module(token_info_t * info, const char* path_to_pkcs11_library); +int get_slot_with_card(token_info_t * info); +void close_pkcs11_module(); + + +#endif //P11TEST_LOADER_H diff --git a/src/tests/p11test/runtest.sh b/src/tests/p11test/runtest.sh new file mode 100755 index 00000000..5e6742b1 --- /dev/null +++ b/src/tests/p11test/runtest.sh @@ -0,0 +1,145 @@ +#!/bin/bash +# runtest.sh: Run test on existing card with possible initialization +# +# Copyright (C) 2016, 2017 Red Hat, Inc. +# +# Author: Jakub Jelen +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser 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 . + +#set -x +SOPIN="12345678" +PIN="123456" +export GNUTLS_PIN=$PIN +GENERATE_KEYS=1 +PKCS11_TOOL="../../tools/pkcs11-tool"; + +function generate_cert() { + TYPE="$1" + ID="$2" + LABEL="$3" + + # Generate key pair + $PKCS11_TOOL --keypairgen --key-type="$TYPE" --login --pin=$PIN \ + --module="$P11LIB" --label="$LABEL" --id=$ID + + if [[ "$?" -ne "0" ]]; then + echo "Couldn't generate $TYPE key pair" + return 1 + fi + + # check type value for the PKCS#11 URI (RHEL7 is using old "object-type") + TYPE_KEY="type" + p11tool --list-all --provider="$P11LIB" --login | grep "object-type" && \ + TYPE_KEY="object-type" + + # Generate certificate + certtool --generate-self-signed --outfile="$TYPE.cert" --template=cert.cfg \ + --provider="$P11LIB" --load-privkey "pkcs11:object=$LABEL;$TYPE_KEY=private" \ + --load-pubkey "pkcs11:object=$LABEL;$TYPE_KEY=public" + # convert to DER: + openssl x509 -inform PEM -outform DER -in "$TYPE.cert" -out "$TYPE.cert.der" + # Write certificate + #p11tool --login --write --load-certificate="$TYPE.cert" --label="$LABEL" \ + # --provider="$P11LIB" + $PKCS11_TOOL --write-object "$TYPE.cert.der" --type=cert --id=$ID \ + --label="$LABEL" --module="$P11LIB" + + rm "$TYPE.cert" "$TYPE.cert.der" + + p11tool --login --provider="$P11LIB" --list-all +} + +function card_setup() { + ECC_KEYS=1 + case $1 in + "softhsm") + P11LIB="/usr/lib64/pkcs11/libsofthsm2.so" + echo "directories.tokendir = .tokens/" > .softhsm2.conf + mkdir ".tokens" + export SOFTHSM2_CONF=".softhsm2.conf" + # Init token + softhsm2-util --init-token --slot 0 --label "SC test" --so-pin="$SOPIN" --pin="$PIN" + ;; + "opencryptoki") + # Supports only RSA mechanisms + ECC_KEYS=0 + P11LIB="/usr/lib64/pkcs11/libopencryptoki.so" + SO_PIN=87654321 + SLOT_ID=3 # swtok slot + systemctl is-active pkcsslotd > /dev/null + if [[ "$?" -ne "0" ]]; then + echo "Opencryptoki needs pkcsslotd running" + exit 1 + fi + groups | grep pkcs11 > /dev/null + if [[ "$?" -ne "0" ]]; then + echo "Opencryptoki requires the user to be in pkcs11 group" + exit 1 + fi + echo "test_swtok" | /usr/sbin/pkcsconf -I -c $SLOT_ID -S $SO_PIN + /usr/sbin/pkcsconf -u -c $SLOT_ID -S $SO_PIN -n $PIN + ;; + "readonly") + GENERATE_KEYS=0 + if [[ ! -z "$2" && -f "$2" ]]; then + P11LIB="$2" + else + P11LIB="/usr/lib64/pkcs11/opensc-pkcs11.so" + P11LIB="../pkcs11/.libs/opensc-pkcs11.so" + fi + ;; + *) + echo "Error: Missing argument." + echo " Usage:" + echo " runtest.sh [softhsm|opencryptoki|readonly [pkcs-library.so]]" + exit 1; + ;; + esac + + if [[ $GENERATE_KEYS -eq 1 ]]; then + # Generate 1024b RSA Key pair + generate_cert "RSA:1024" "01" "RSA_auth" + # Generate 2048b RSA Key pair + generate_cert "RSA:2048" "02" "RSA2048" + if [[ $ECC_KEYS -eq 1 ]]; then + # Generate 256b ECC Key pair + generate_cert "EC:secp256r1" "03" "ECC_auth" + # Generate 521b ECC Key pair + generate_cert "EC:secp521r1" "04" "ECC521" + fi + fi +} + +function card_cleanup() { + case $1 in + "softhsm") + rm .softhsm2.conf + rm -rf ".tokens" + ;; + esac +} + +card_setup "$@" + +make p11test || exit +if [[ "$PKCS11SPY" -ne "" ]]; then + export PKCS11SPY="$P11LIB" + $VALGRIND ./p11test -m /usr/lib64/pkcs11/pkcs11-spy.so -p $PIN +else + #bash + $VALGRIND ./p11test -m "$P11LIB" -o test.json -p $PIN +fi + +card_cleanup "$@" diff --git a/win32/Make.rules.mak b/win32/Make.rules.mak index 573e125e..18ac1ff6 100644 --- a/win32/Make.rules.mak +++ b/win32/Make.rules.mak @@ -20,6 +20,9 @@ WIXVSVER = VS2015 WIX_INCL_DIR = "/I$(WIX)\SDK\$(WIXVSVER)\inc" WIX_LIBS = "$(WIX)\SDK\$(WIXVSVER)\lib\$(PLATFORM)\dutil.lib" "$(WIX)\SDK\$(WIXVSVER)\lib\$(PLATFORM)\wcautil.lib" +# We do not build tests on windows +#TESTS_DEF = /DENABLE_TESTS + #Include support for Secure Messaging SM_DEF = /DENABLE_SM @@ -137,10 +140,10 @@ ALL_INCLUDES = /I$(TOPDIR)\win32 /I$(TOPDIR)\src $(OPENPACE_INCL_DIR) $(OPENSSL_ !IF "$(DEBUG_DEF)" == "/DDEBUG" LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMT /DEBUG CODE_OPTIMIZATION = -COPTS = /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /MTd /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /DDEBUG /Zi /Od +COPTS = /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /MTd /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) $(TESTS_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /DDEBUG /Zi /Od !ELSE LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMTD /DEBUG /OPT:REF /OPT:ICF -COPTS = /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /MT /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /Zi +COPTS = /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /MT /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) $(TESTS_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /Zi !ENDIF