PKCS#11 testsuite (#1224)

* Initial version of pkcs11 testsuite

* Refactor test cases to several files, clean up awful and unused stuff

* Static mechanism list based on the actual token offer

* Get rid of magic numbers

* Documentation

* License update based on the original project

* Verbose readme

* Cleanup unused code, long lines and method order

* Typo; More verbose errors

* Use fallback mechanisms

* Refactor object allocation and certificate search

* PKCS11SPY mentioned, more TODO

* add SHA mechanisms

* Do not try to Finalize already finalized cryptoki

* Add more flags and mechanisms

* Do not list table for no results

* Logical order of the tests (regression last)

* read ALWAYS_AUTHENTICATE from correct place

* ALWAYS_AUTHENTICATE for decryption

* Test EC key length signature based on the actual key length

* Shorten CKM_ list output, add keygen types detection

* Skip decrypting on non-supported mechanisms

* Fail hard if the C_Login fails

* Reorganize local FLAGS_ constants

* Test RSA Digest mechanisms

* Correct mechanisms naming, typos

* Do not attempt to do signature using empty keys

* CKM_ECDSA_SHA1 support

* Correct type cast when getting attributes

* Report failures from all mechanisms

* Standardize return values, eliminate complete fails, documentation interface

* Wait for slot event test

* Add switch to allow interaction with a card (WaitForSlotEvent)

* At least try to verify using C_Verify, if it fails, fall back to openssl

* Get rid of function_pointers

* Get rid of additional newline

* Share always_authenticate() function between the test cases

* Refactor Encrypt&decrypt test to functions

* Do not overwrite bits if they are not provided by CKA, indentation

* Cleanup and Break to more functions Sign&Verify test

* CKM_RSA_X_509 sign and verify with openssl padding

* More TODO's

* Proper abstracted padding with RSA_X_509 mechanism

* Add ongoing tasks from different TODO list

* Update instructions. Another todo

* Variables naming

* Increase mechanism list size, use different static buffers for flags and mechanism names

* nonstandard mechanism CKM_SHA224_RSA_PKCS supported by some softotkens

* Get rid of loop initial declarations

* Loop initial declaration, typos, strict warnings

* Move the p11test to the new folder to avoid problems with dynamically linked opensc.so

* Update path in README

* Possibility to validate the testsuite agains software tokens

* Add possibility to select slot ID on command-line (when there are more cards present)

* Clean up readme to reflect current options and TODOs

* Do not attempt to use keys without advertised sign&verify bits to avoid false positives

* Get and present more object attributes in readonly test; refactor table

* New test checking if the set of attributes (usage flags) is reasonable

* Test multipart signatures. There is not reasonable mechanism supporting multipart encryption

* Use PKCS#11 encryption if possible (with openssl fallback)

* Identify few more mechanisms (PSS) in the lest

* Resize table to fit new mechanisms

* Remove initial loop declaration from multipart test

* Use pkcs11-tool instead of p11tool form most of the operations (master have most of the features)

* Preparation for machine readable results

* Refactor log variables out of the main context, try to export generic data

* Do not write to non-existing FD if not logging

* Export missing data into the log file in JSON

* Store database in json

* Sanity check

* Avoid uninitialized structure fields using in state structure

* Dump always_authenticate attribute too

* Manual selection of slots with possibility to use slots without tokens

* Do not free before finalizing

* Proper cleanup of message in all cases

* Proper allocation and deallocation of messages

* Sanitize missing cases (memory leaks)

* Suppressions for testing under valgrind

* Better handling message_lengt during sign&verify (avoid invalid access)

* Suppress another PCSC error

* Do not use default PIN. Fail if none specified

* Sanitize initialization. Skip incomplete key pairs

* Add missing newline in errors

* Fix condition for certificate search

* Avoid several calls for attributes of zero length

* Handle if the private key is not present on the card

* Improve memory handling, silent GCC warning of 'unused' variable

* Fail early with missing private key, cleanup the messages

* Use correct padding for encryption

* Cache if the card supports Verify/Encrypt and avoid trying over and over again

* Loosen the condition for the Usage flags

* OpenSSL 1.1.0 compatibility

* Add missing mechanisms

* Do not require certificates on the card and pass valid data for RSA_PKCS mechanisms

* Add missing PIN argument in runtest.sh

* Add OpenSSL < 1.1 comatible bits

* Add SHA2 ECDSA mechanisms handling

* Use public key from PKCS#11 if the certificate is missing (or compare it with certificate)

* Avoid long definitions in OpenSSL compat layer

* In older OpenSSL, the header file is ecdsa.h

* Add missing config.h to apply compat OpenSSL layer

* ASN1_STRING_get0_data() is also new in 1.1.0

* Return back RSA_X_509 mechanism

* Drop bogus CKM_* in the definitions

* Drop CKM_SHA224_RSA_PKCS as it is already in pkcs11.h

* Update documentation

* Use NDEBUG as intended

* typos, cleanup

* Typos, cleanup, update copyright

* Additional check for OpenCryptoki, generate more key types on soft tokens

* Prepare for RSA-PSS and RSA-OAEP

* Use usage&result flags for the tests, gracefully ignore PSS&OAEP

* pkcs11.h: Add missing definitions for PSS

* PSS and OAEP tests

readonly: Typos, reformat

* Working version, memory leak

* Tweak message lengths for OAEP and PSS

* Skip tests that are not aplicable for tokens

* configure.ac: New switch --enable-tests

Do not attempt to build tests if cmocka is not available or
--enable-tests is provided. It makes also more lightweight release
builds out of the box (or with --disable-tests).

* travis: Install cmocka if not available

* Do not build tests on Windows and make dist pass

* Try to install cmocka from apt and from brew

* Do not require sudo (cmocka from apt and brew works)
This commit is contained in:
Jakub Jelen 2018-05-18 12:31:55 +02:00 committed by Frank Morgner
parent eb60481f89
commit 9858d05589
37 changed files with 4126 additions and 9 deletions

1
.gitignore vendored
View File

@ -128,5 +128,6 @@ src/tests/lottery
src/tests/p15dump
src/tests/pintest
src/tests/prngtest
src/tests/p11test/p11test
version.m4.ci

View File

@ -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:

View File

@ -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}

View File

@ -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

View File

@ -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 $@"

View File

@ -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 <openssl/dsa.h>
#endif
#ifndef OPENSSL_NO_EC
#include <openssl/ecdsa.h>
#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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,6 @@
organization = "OpenSC"
expiration_days = 365
email = "none@example.org"
signing_key
encryption_key

132
src/tests/p11test/p11test.c Normal file
View File

@ -0,0 +1,132 @@
/*
* p11test.c: Test suite for PKCS#11 API
*
* Copyright (C) 2016 Martin Strhársky <strharsky.martin@gmail.com>
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <getopt.h>
#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);
}

View File

@ -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
}

View File

@ -0,0 +1,728 @@
/*
* p11test_case_common.c: Functions shared between test cases.
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -0,0 +1,127 @@
/*
* p11test_case_common.h: Functions shared between test cases.
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef P11TEST_CASE_COMMON_H
#define P11TEST_CASE_COMMON_H
#include <openssl/x509.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#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 */

View File

@ -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 <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -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 <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_common.h"
#include "p11test_case_readonly.h"
void ec_sign_size_test(void **state);

View File

@ -0,0 +1,138 @@
/*
* p11test_case_mechs.c: Check mechanisms supported by token
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -0,0 +1,24 @@
/*
* p11test_case_mechs.h: Check mechanisms supported by token
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_common.h"
void supported_mechanisms_test(void **state);

View File

@ -0,0 +1,107 @@
/*
* p11test_case_multipart.c: Multipart Sign & Verify tests (RSA only)
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -0,0 +1,26 @@
/*
* p11test_case_multipart.h: Multipart Sign & Verify tests (RSA only)
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_common.h"
#include "p11test_case_readonly.h"
void multipart_tests(void **state);

View File

@ -0,0 +1,874 @@
/*
* p11test_case_pss_oaep.c: RSA-PSS and RSA-OAEP tests
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_pss_oaep.h"
#include "libopensc/internal.h"
#include <openssl/sha.h>
#include <openssl/evp.h>
#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);
}

View File

@ -0,0 +1,24 @@
/*
* p11test_case_pss_oaep.h: RSA-PSS and RSA-OAEP tests
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_common.h"
void pss_oaep_test(void **state);

View File

@ -0,0 +1,699 @@
/*
* p11test_case_readonly.c: Sign & Verify tests
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_readonly.h"
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/ripemd.h>
#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);
}

View File

@ -0,0 +1,29 @@
/*
* p11test_case_readonly.h: Sign & Verify tests
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);

View File

@ -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 <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -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 <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_common.h"
#include "p11test_case_readonly.h"
void usage_test(void **state);

View File

@ -0,0 +1,59 @@
/*
* p11test_case_wait.c: Test slot events (insert / remove)
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -0,0 +1,24 @@
/*
* p11test_case_wait.h: Test slot events (insert / remove)
*
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "p11test_case_common.h"
void wait_test(void **state);

View File

@ -0,0 +1,89 @@
/*
* p11test_common.h: Test suite shared declarations for PKCS#11 API
*
* Copyright (C) 2016 Martin Strhársky <strharsky.martin@gmail.com>
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef P11TEST_COMMON_H
#define P11TEST_COMMON_H
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <cmocka.h>
#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 */

View File

@ -0,0 +1,207 @@
/*
* p11test_helpers.c: Test suite for PKCS#11 API: Supporting functions
*
* Copyright (C) 2016 Martin Strhársky <strharsky.martin@gmail.com>
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View File

@ -0,0 +1,37 @@
/*
* p11test_helpers.h: Test suite for PKCS#11 API: Supporting functions
*
* Copyright (C) 2016 Martin Strhársky <strharsky.martin@gmail.com>
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -0,0 +1,147 @@
/*
* p11test_loader.c: Library loader for PKCS#11 test suite
*
* Copyright (C) 2016 Martin Strhársky <strharsky.martin@gmail.com>
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -0,0 +1,34 @@
/*
* p11test_loader.h: Library loader for PKCS#11 test suite
*
* Copyright (C) 2016 Martin Strhársky <strharsky.martin@gmail.com>
* Copyright (C) 2016, 2017 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef P11TEST_LOADER_H
#define P11TEST_LOADER_H
#include <dlfcn.h>
#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

145
src/tests/p11test/runtest.sh Executable file
View File

@ -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 <jjelen@redhat.com>
#
# 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 <http://www.gnu.org/licenses/>.
#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 "$@"

View File

@ -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