Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion pkcs11/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down
108 changes: 108 additions & 0 deletions pkcs11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,114 @@ See [PKCS11.md](./PKCS11.md) in this folder.
It should be noted WOLFSSL_PKCS11_RW_TOKENS is only needed for adding the keys and certs to the store. Once already in the store this is no longer needed.


## Setting up and testing OP-TEE

[OP-TEE](https://optee.readthedocs.io/) provides a PKCS #11 trusted
application, so private keys can be generated and used inside the TrustZone
secure world and never appear in normal-world memory. This has been tested on
an NXP i.MX95 running OP-TEE 4.4, but nothing here is board specific.

1. Build OP-TEE with the PKCS #11 trusted application

The TA is not in every OP-TEE build. Enable it with `CFG_PKCS11_TA=y` and
install the resulting `fd02c9da-306c-48c7-a49c-bbd827ae86ee.ta` where
`tee-supplicant` looks for TAs. You also need the `optee_client` userspace:
`tee-supplicant`, `libteec.so.2` and `libckteec.so.0`.

2. Make sure `tee-supplicant` is running

Every PKCS #11 call fails at `C_Initialize` without it, because the TA
cannot be loaded. If the libraries are not in the default library path,
point the loader at them:

```
export LD_LIBRARY_PATH=/path/to/optee/lib
tee-supplicant -l /path/to/ta/dir &
```

3. Change to wolfssl directory

```
./autogen.sh
./configure --enable-pkcs11 --enable-cryptocb-rsa-pad
make
sudo make install
```

`--enable-cryptocb-rsa-pad` matters. Without it wolfSSL asks the token for
raw RSA (`CKM_RSA_X_509`), which OP-TEE's TA does not implement, and RSA
private key operations fail with `RSA_BUFFER_E` (-131). With it wolfSSL
uses `CKM_RSA_PKCS` and the RSA examples pass. The same applies to any
token that declines raw RSA.

4. Change to wolfssl-examples/pkcs11 directory and build

```
make
```

5. Initialize the token and run the examples

OP-TEE tokens come up uninitialized and OP-TEE ships no equivalent of
`softhsm2-util`, so `pkcs11_inittoken` does it through the PKCS #11 API:

```
./optee-init.sh
```

That initializes slot 0 and then runs the examples. To use a different
slot, label or PIN:

```
OPTEE_TOKEN=myToken OPTEE_PIN=1234 ./optee-init.sh 1
```

Once the token is initialized, run the examples on their own with:

```
./optee.sh
```

Or a single example directly, with the usual argument order:

```
./pkcs11_genecc libckteec.so.0 0 wolfSSL cryptoki
```

### EC keys that both derive and sign

`pkcs11_test` generates a single EC key and then uses it for both ECDH and
ECDSA. PKCS #11 leaves the defaults for `CKA_DERIVE` and `CKA_SIGN` up to the
token: SoftHSM grants both regardless of the template, while OP-TEE grants only
what was asked for.

The examples therefore request both explicitly:

```c
wc_ecc_make_key_ex2(&rng, 32, key, ECC_CURVE_DEF, EC_KEYGEN_FLAGS);
```

`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.

## TLS Server Example with SoftHSM (RSA)

The example `server-tls-pkcs11` is a server that uses a private key that has been stored on the PKCS #11 device.
Expand Down
48 changes: 48 additions & 0 deletions pkcs11/optee-init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh

# Initialize an OP-TEE PKCS#11 token and then run the examples against it.
#
# OP-TEE tokens come up uninitialized and OP-TEE ships no equivalent of
# softhsm2-util, so pkcs11_inittoken does it through the PKCS#11 API. Re-running
# this is safe: an already-initialized token is left alone.

set -e

cd "$(dirname "$0")"

# Same argument convention as optee.sh: an optional slot id first, then any
# specific examples to run.
# 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
OPTEE_LIB=libckteec.so.0
fi
if [ -z "$OPTEE_SLOTID" ]
then
OPTEE_SLOTID=0
fi
if [ -z "$OPTEE_TOKEN" ]
then
OPTEE_TOKEN=wolfSSL
fi
if [ -z "$OPTEE_SOPIN" ]
then
OPTEE_SOPIN=cryptoki
fi
if [ -z "$OPTEE_PIN" ]
then
OPTEE_PIN=cryptoki
fi

./pkcs11_inittoken "$OPTEE_LIB" "$OPTEE_SLOTID" "$OPTEE_TOKEN" \
"$OPTEE_SOPIN" "$OPTEE_PIN"

OPTEE_LIB="$OPTEE_LIB" OPTEE_TOKEN="$OPTEE_TOKEN" OPTEE_PIN="$OPTEE_PIN" \
exec ./optee.sh "$OPTEE_SLOTID" "$@"
83 changes: 83 additions & 0 deletions pkcs11/optee.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/sh

# Run the PKCS#11 examples against OP-TEE's PKCS#11 trusted application.
#
# The token must already be initialized - use ./optee-init.sh for that.
#
# Requires tee-supplicant to be running; without it every call fails at
# C_Initialize because the TA cannot be loaded.

# 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
# case set OPTEE_LIB (and LD_LIBRARY_PATH) to point at it.
if [ -z "$OPTEE_LIB" ]
then
OPTEE_LIB=libckteec.so.0
fi

if [ -z "$OPTEE_SLOTID" ]
then
OPTEE_SLOTID=0
fi
if [ -z "$OPTEE_TOKEN" ]
then
OPTEE_TOKEN=wolfSSL
fi
if [ -z "$OPTEE_PIN" ]
then
OPTEE_PIN=cryptoki
fi

rc=0

run_example()
{
name=$1
shift
echo
echo "# $name"
if ! "$@" "$OPTEE_LIB" "$OPTEE_SLOTID" "$OPTEE_TOKEN" "$OPTEE_PIN"
then
echo "# FAILED: $name"
rc=1
fi
}

echo "# Using slot ID: $OPTEE_SLOTID"
echo "# Using library: $OPTEE_LIB"
echo "# Using token: $OPTEE_TOKEN"

if [ $# -gt 0 ]
then
for example in "$@"
do
run_example "$example" "./$example"
done
else
run_example "RSA example" ./pkcs11_rsa
run_example "ECC example" ./pkcs11_ecc
run_example "Generate ECC example" ./pkcs11_genecc
run_example "AES-GCM example" ./pkcs11_aesgcm
run_example "AES-CBC example" ./pkcs11_aescbc
run_example "HMAC example" ./pkcs11_hmac
run_example "Random Number Generation example" ./pkcs11_rand
run_example "PKCS#11 test" ./pkcs11_test
fi

echo
if [ $rc -eq 0 ]
then
echo "# All PKCS#11 examples passed"
else
echo "# One or more PKCS#11 examples FAILED"
fi

exit $rc
Loading
Loading