pkcs11: link -ldl for the token init example and only take a numeric slot arg

pull/618/head
David Garske 2026-08-19 12:18:25 -07:00
parent 803d9c8226
commit 28a6f5b8b1
6 changed files with 118 additions and 34 deletions

View File

@ -2,7 +2,9 @@
CC = gcc
WOLFSSL_INSTALL_DIR = /usr/local
CFLAGS = -Wall -I$(WOLFSSL_INSTALL_DIR)/include
LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lm
# -ldl for pkcs11_inittoken, which dlopen()s the PKCS#11 library. Harmless
# on glibc >= 2.34 where libdl is merged into libc, required on older ones.
LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lm -ldl
# option variables
DYN_LIB = -lwolfssl
@ -11,9 +13,28 @@ DEBUG_FLAGS = -g -DDEBUG
DEBUG_INC_PATHS = -MD
OPTIMIZE = -Os
# WC_ECC_FLAG_DERIVE is an enum member, not a macro, so the preprocessor cannot
# test for it - and a version check cannot either, because wolfSSL master and
# v5.9.2-stable both report LIBWOLFSSL_VERSION_HEX 0x05009002. Probe by
# compiling against the installed headers instead, which is accurate on any
# release, snapshot or git build.
# Skipped for goals that never compile C, so "make clean" does not spawn a
# throwaway compile - which would also fail noisily where wolfSSL is absent.
ifeq ($(filter clean,$(MAKECMDGOALS)),)
HAVE_ECC_FLAG_DERIVE := $(shell printf '%s\n' \
'#include <wolfssl/options.h>' \
'#include <wolfssl/wolfcrypt/ecc.h>' \
'int main(void){return (int)WC_ECC_FLAG_DERIVE;}' \
| $(CC) -I$(WOLFSSL_INSTALL_DIR)/include -x c - -o /dev/null 2>/dev/null \
&& echo yes)
endif
# Options
#CFLAGS+=$(DEBUG_FLAGS)
CFLAGS+=$(OPTIMIZE)
ifeq ($(HAVE_ECC_FLAG_DERIVE),yes)
CFLAGS+=-DHAVE_WC_ECC_FLAG_DERIVE
endif
#LIBS+=$(STATIC_LIB) -ldl -lm
LIBS+=$(DYN_LIB)

View File

@ -243,14 +243,26 @@ what was asked for.
The examples therefore request both explicitly:
```c
wc_ecc_make_key_ex2(&rng, 32, key, ECC_CURVE_DEF,
WC_ECC_FLAG_DEC_SIGN | WC_ECC_FLAG_DERIVE);
wc_ecc_make_key_ex2(&rng, 32, key, ECC_CURVE_DEF, EC_KEYGEN_FLAGS);
```
`WC_ECC_FLAG_DERIVE` requires wolfSSL with PKCS #11 derive+sign key generation
support. Against an older wolfSSL, drop that flag; `pkcs11_test` will then fail
on a strict token at the first ECDSA operation with
`CKR_KEY_FUNCTION_NOT_PERMITTED`, surfacing as `WC_HW_E` (-248).
`EC_KEYGEN_FLAGS` is `WC_ECC_FLAG_DEC_SIGN | WC_ECC_FLAG_DERIVE` when the
installed wolfSSL has `WC_ECC_FLAG_DERIVE`, and `WC_ECC_FLAG_DEC_SIGN` alone
when it does not. No edit is needed either way: the Makefile probes for the
flag by compiling against the installed headers and defines
`HAVE_WC_ECC_FLAG_DERIVE` when it is present.
The probe exists because neither of the usual tests works here.
`WC_ECC_FLAG_DERIVE` is an enum member rather than a macro, so `#ifdef` cannot
see it, and a version test cannot distinguish the two cases either, because
wolfSSL master and v5.9.2-stable both report `LIBWOLFSSL_VERSION_HEX`
`0x05009002`.
Against a wolfSSL without the flag the key is generated sign-only, which is all
that library can request. `pkcs11_test` then fails on a strict token - the
OP-TEE TA among them - at the first ECDH operation with
`CKR_KEY_FUNCTION_NOT_PERMITTED`, surfacing as `WC_HW_E` (-248). Tokens that
enable `CKA_DERIVE` by default are unaffected.
All the examples pass, including RSA key generation, ECDSA, ECDH, AES-CBC,
AES-GCM, HMAC and RNG.

View File

@ -12,11 +12,13 @@ cd "$(dirname "$0")"
# Same argument convention as optee.sh: an optional slot id first, then any
# specific examples to run.
if [ $# -gt 0 ]
then
OPTEE_SLOTID=$1
shift
fi
# Only treat the first argument as a slot id if it is numeric, so that
# "./optee-init.sh pkcs11_rsa" runs one example against the default slot instead of
# silently consuming the example name as a slot id.
case "${1:-}" in
'' | *[!0-9]* ) ;;
* ) OPTEE_SLOTID=$1; shift ;;
esac
if [ -z "$OPTEE_LIB" ]
then

View File

@ -7,11 +7,13 @@
# Requires tee-supplicant to be running; without it every call fails at
# C_Initialize because the TA cannot be loaded.
if [ $# -gt 0 ]
then
OPTEE_SLOTID=$1
shift
fi
# Only treat the first argument as a slot id if it is numeric, so that
# "./optee.sh pkcs11_rsa" runs one example against the default slot instead of
# silently consuming the example name as a slot id.
case "${1:-}" in
'' | *[!0-9]* ) ;;
* ) OPTEE_SLOTID=$1; shift ;;
esac
# OP-TEE's PKCS#11 client library. It is usually installed as a normal shared
# library, but on an embedded rootfs it is often staged elsewhere, in which

View File

@ -28,7 +28,8 @@
* through the PKCS#11 API itself, so it works against any implementation and
* needs nothing installed on the target beyond the PKCS#11 library.
*
* It is safe to re-run: an already-initialized token is left untouched.
* It is safe to re-run: a fully initialized token is left untouched, and one
* left half-initialized by an interrupted run is completed rather than wiped.
*
* Note this deliberately talks to the PKCS#11 library directly rather than
* going through wolfSSL. Token initialization is administrative, not
@ -49,6 +50,9 @@
#ifndef CKF_TOKEN_INITIALIZED
#define CKF_TOKEN_INITIALIZED 0x00000400UL
#endif
#ifndef CKF_USER_PIN_INITIALIZED
#define CKF_USER_PIN_INITIALIZED 0x00000008UL
#endif
/* PKCS#11 labels are a fixed-width, space-padded field - not a C string. */
#define LABEL_SZ 32
@ -77,26 +81,41 @@ static int init_token(CK_FUNCTION_LIST* func, CK_SLOT_ID slotId,
return 1;
}
if ((tokenInfo.flags & CKF_TOKEN_INITIALIZED) != 0) {
/* Initialization is two steps that can be interrupted between: C_InitToken
* sets the SO PIN and marks the token initialized, and only a later SO
* login can set the user PIN. Treat the token as done only when both have
* happened, so a run that died in between can be completed by re-running
* rather than needing the token wiped. */
if ((tokenInfo.flags & CKF_TOKEN_INITIALIZED) != 0 &&
(tokenInfo.flags & CKF_USER_PIN_INITIALIZED) != 0) {
printf("Token in slot %lu is already initialized - nothing to do\n",
(unsigned long)slotId);
return 0;
}
memset(padded, ' ', sizeof(padded));
memcpy(padded, label, labelSz);
if ((tokenInfo.flags & CKF_TOKEN_INITIALIZED) == 0) {
memset(padded, ' ', sizeof(padded));
memcpy(padded, label, labelSz);
/* Sets the SO PIN and the label, and puts the token in a state where the
* SO can log in to set the user PIN. */
rv = func->C_InitToken(slotId, (CK_UTF8CHAR_PTR)soPin,
(CK_ULONG)strlen(soPin), padded);
if (rv != CKR_OK) {
fprintf(stderr, "Failed to initialize token: 0x%lx\n",
(unsigned long)rv);
return 1;
/* Sets the SO PIN and the label, and puts the token in a state where
* the SO can log in to set the user PIN. */
rv = func->C_InitToken(slotId, (CK_UTF8CHAR_PTR)soPin,
(CK_ULONG)strlen(soPin), padded);
if (rv != CKR_OK) {
fprintf(stderr, "Failed to initialize token: 0x%lx\n",
(unsigned long)rv);
return 1;
}
printf("Initialized token in slot %lu with label \"%s\"\n",
(unsigned long)slotId, label);
}
else {
/* Resuming: re-running C_InitToken here would destroy every object
* already on the token, so pick up at the user PIN instead. The SO PIN
* must match the one the earlier run set. */
printf("Token in slot %lu is initialized but has no user PIN"
" - setting it\n", (unsigned long)slotId);
}
printf("Initialized token in slot %lu with label \"%s\"\n",
(unsigned long)slotId, label);
/* The user PIN can only be set by the SO, over a read/write session. */
rv = func->C_OpenSession(slotId, CKF_SERIAL_SESSION | CKF_RW_SESSION,
@ -139,6 +158,8 @@ int main(int argc, char* argv[])
CK_SLOT_ID slotId;
CK_RV rv;
int ret;
unsigned long slotVal;
char* slotEnd;
if (argc != 6) {
fprintf(stderr, "Usage: pkcs11_inittoken <libname> <slot> <tokenname>"
@ -146,7 +167,15 @@ int main(int argc, char* argv[])
return 1;
}
slotId = (CK_SLOT_ID)atoi(argv[2]);
/* strtoul rather than atoi: atoi returns 0 for non-numeric input, which
* would silently initialize slot 0 - the wrong token, destructively. */
slotEnd = NULL;
slotVal = strtoul(argv[2], &slotEnd, 10);
if (slotEnd == argv[2] || *slotEnd != '\0') {
fprintf(stderr, "Slot must be a number: %s\n", argv[2]);
return 1;
}
slotId = (CK_SLOT_ID)slotVal;
dlib = dlopen(argv[1], RTLD_NOW);
if (dlib == NULL) {

View File

@ -27,6 +27,24 @@
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/logging.h>
/* Ask the token for derive as well as sign when generating the EC key.
*
* WC_ECC_FLAG_DERIVE is an enum member added to wolfSSL after 5.9.2, so the
* preprocessor cannot test for it directly, and a version test does not help
* either: 5.9.2-stable and current master both report LIBWOLFSSL_VERSION_HEX
* 0x05009002. The Makefile probes for it by compiling against the installed
* headers and defines HAVE_WC_ECC_FLAG_DERIVE when it is present.
*
* Without it the key is generated sign-only, which is all a wolfSSL that
* predates the flag can request. The ECDH test then fails on a token that
* grants only what was asked for, such as the OP-TEE PKCS#11 TA; tokens that
* enable CKA_DERIVE by default are unaffected. */
#ifdef HAVE_WC_ECC_FLAG_DERIVE
#define EC_KEYGEN_FLAGS (WC_ECC_FLAG_DEC_SIGN | WC_ECC_FLAG_DERIVE)
#else
#define EC_KEYGEN_FLAGS (WC_ECC_FLAG_DEC_SIGN)
#endif
#ifndef NO_RSA
static const unsigned char client_key_der_2048[] =
{
@ -560,7 +578,7 @@ int gen_ec_keys_label(Pkcs11Token* token, ecc_key* key, char* label, int devId)
* defaults up to the token, so a token that grants only what was
* asked for refuses the other operation. */
ret = wc_ecc_make_key_ex2(&rng, 32, key, ECC_CURVE_DEF,
WC_ECC_FLAG_DEC_SIGN | WC_ECC_FLAG_DERIVE);
EC_KEYGEN_FLAGS);
if (ret != 0)
fprintf(stderr, "Failed to generate EC key: %d\n", ret);
}
@ -581,7 +599,7 @@ int gen_ec_keys(Pkcs11Token* token, ecc_key* key, unsigned char* id, int idLen,
* defaults up to the token, so a token that grants only what was
* asked for refuses the other operation. */
ret = wc_ecc_make_key_ex2(&rng, 32, key, ECC_CURVE_DEF,
WC_ECC_FLAG_DEC_SIGN | WC_ECC_FLAG_DERIVE);
EC_KEYGEN_FLAGS);
if (ret != 0)
fprintf(stderr, "Failed to generate EC key: %d\n", ret);
}