diff mbox series

[2/5] Increased PKCS#11 decryption performance with p11-kit

Message ID 20251219112215.103862-3-bage@debian.org
State Accepted
Headers show
Series pkcs11 decrypt provider based on p11-kit | expand

Commit Message

Bastian Germann Dec. 19, 2025, 11:21 a.m. UTC
From: Matej Zachar <zachar.matej@gmail.com>

On platforms using "opTee + pkcs11 TA + RPMB" the decryption
speed is increased by order of magnitude - as the C_DecryptInit
context is not lost between decryption updates (C_DecryptUpdate).
On my board (iMX8MP) it went from 50min with wolfSSL to 3min with p11-kit

Signed-off-by: Matej Zachar <zachar.matej@gmail.com>
---
 Makefile.flags                          |   2 -
 crypto/Kconfig                          |  16 +-
 crypto/Makefile                         |   6 +-
 crypto/swupdate_decrypt_pkcs11_p11kit.c | 290 ++++++++++++++++++++++++
 test/Makefile                           |  32 ++-
 test/data/token/softhsm.conf            |   2 +
 test/test_crypt_pkcs11.c                |  98 ++++++++
 7 files changed, 429 insertions(+), 17 deletions(-)
 create mode 100644 crypto/swupdate_decrypt_pkcs11_p11kit.c
 create mode 100644 test/data/token/softhsm.conf
 create mode 100644 test/test_crypt_pkcs11.c

Comments

Stefano Babic Jan. 11, 2026, 10:11 a.m. UTC | #1
Hi Bastian, Matej,

On 12/19/25 12:21, Bastian Germann wrote:
> From: Matej Zachar <zachar.matej@gmail.com>
> 
> On platforms using "opTee + pkcs11 TA + RPMB" the decryption
> speed is increased by order of magnitude - as the C_DecryptInit
> context is not lost between decryption updates (C_DecryptUpdate).
> On my board (iMX8MP) it went from 50min with wolfSSL to 3min with p11-kit
> 
> Signed-off-by: Matej Zachar <zachar.matej@gmail.com>
> ---
>   Makefile.flags                          |   2 -
>   crypto/Kconfig                          |  16 +-
>   crypto/Makefile                         |   6 +-
>   crypto/swupdate_decrypt_pkcs11_p11kit.c | 290 ++++++++++++++++++++++++
>   test/Makefile                           |  32 ++-
>   test/data/token/softhsm.conf            |   2 +
>   test/test_crypt_pkcs11.c                |  98 ++++++++
>   7 files changed, 429 insertions(+), 17 deletions(-)
>   create mode 100644 crypto/swupdate_decrypt_pkcs11_p11kit.c
>   create mode 100644 test/data/token/softhsm.conf
>   create mode 100644 test/test_crypt_pkcs11.c
> 

I full agree to discouple PKCS11 from a specific implementation like 
WolfSSL, but I guess I cannot test this myself before merging. I would 
like that someone sends your tested-by for this series or this patch.

Nevertheless, this does not break anything, and it can be safe merged.

Reviewed-by: Stefano Babic <stefano.babic@swupdate.org>

Stefano

> diff --git a/Makefile.flags b/Makefile.flags
> index 65f112e8..40dd3b66 100644
> --- a/Makefile.flags
> +++ b/Makefile.flags
> @@ -163,8 +163,6 @@ endif
>   ifeq ($(CONFIG_SSL_IMPL_WOLFSSL),y)
>   KBUILD_CPPFLAGS += -DOPENSSL_ALL
>   LDLIBS += wolfssl
> -else ifeq ($(CONFIG_PKCS11),y)
> -LDLIBS += wolfssl
>   endif
>   
>   ifeq ($(CONFIG_SSL_IMPL_MBEDTLS),y)
> diff --git a/crypto/Kconfig b/crypto/Kconfig
> index 4b9db821..48eeb01c 100644
> --- a/crypto/Kconfig
> +++ b/crypto/Kconfig
> @@ -21,6 +21,10 @@ menu "Crypto libraries"
>   	config SSL_IMPL_GPGME
>   		bool "gpgme"
>   		depends on HAVE_GPGME
> +
> +	config PKCS11
> +		bool "PKCS#11 (p11-kit)"
> +		depends on HAVE_P11KIT
>   endmenu
>   
>   config HASH_VERIFY
> @@ -82,9 +86,9 @@ menu "Encryption"
>   
>   config ENCRYPTED_IMAGES
>   	bool "Images can be encrypted with a symmetric key"
> -	depends on SSL_IMPL_OPENSSL || SSL_IMPL_WOLFSSL || SSL_IMPL_MBEDTLS
> +	depends on SSL_IMPL_OPENSSL || SSL_IMPL_WOLFSSL || SSL_IMPL_MBEDTLS || PKCS11
>   comment "Image encryption needs an SSL implementation"
> -	depends on !SSL_IMPL_OPENSSL && !SSL_IMPL_WOLFSSL && !SSL_IMPL_MBEDTLS
> +	depends on !SSL_IMPL_OPENSSL && !SSL_IMPL_WOLFSSL && !SSL_IMPL_MBEDTLS && !PKCS11
>   
>   config ENCRYPTED_SW_DESCRIPTION
>   	bool "Even sw-description is encrypted"
> @@ -126,13 +130,5 @@ config ENCRYPTED_IMAGES_HARDEN_LOGGING
>   	  hash mismatch and errors in the decryption finalization (padding) of a
>   	  streamed image are suppressed.
>   
> -config PKCS11
> -	bool "Enable PKCS#11 cryptographic operations"
> -	default n
> -	depends on SSL_IMPL_WOLFSSL && HAVE_P11KIT && ENCRYPTED_IMAGES
> -	help
> -	  Enable using PKCS#11 for AES decryption instead of having the plain
> -	  key available in a file. This is implemented with wolfSSL independent
> -	  from the SSL implementation and replaces the plain key method.
>   endmenu
>   
> diff --git a/crypto/Makefile b/crypto/Makefile
> index 25ab3ab7..b591ff57 100644
> --- a/crypto/Makefile
> +++ b/crypto/Makefile
> @@ -14,10 +14,8 @@ endif
>   ifeq ($(CONFIG_SSL_IMPL_WOLFSSL),y)
>   obj-$(CONFIG_HASH_VERIFY)	+= swupdate_HASH_wolfssl.o
>   obj-$(CONFIG_SIGALG_CMS)	+= swupdate_pkcs7_verify_wolfssl.o
> -ifeq ($(CONFIG_PKCS11),y)
>   obj-$(CONFIG_ENCRYPTED_IMAGES)	+= swupdate_decrypt_wolfssl.o
>   endif
> -endif
>   
>   ifeq ($(CONFIG_SSL_IMPL_MBEDTLS),y)
>   obj-$(CONFIG_HASH_VERIFY)	+= swupdate_HASH_mbedtls.o
> @@ -28,3 +26,7 @@ endif
>   ifeq ($(CONFIG_SSL_IMPL_GPGME),y)
>   obj-$(CONFIG_SIGALG_GPG)	+= swupdate_gpg_verify.o
>   endif
> +
> +ifeq ($(CONFIG_PKCS11),y)
> +obj-$(CONFIG_ENCRYPTED_IMAGES)	+= swupdate_decrypt_pkcs11_p11kit.o
> +endif
> diff --git a/crypto/swupdate_decrypt_pkcs11_p11kit.c b/crypto/swupdate_decrypt_pkcs11_p11kit.c
> new file mode 100644
> index 00000000..f66426bc
> --- /dev/null
> +++ b/crypto/swupdate_decrypt_pkcs11_p11kit.c
> @@ -0,0 +1,290 @@
> +// SPDX-FileCopyrightText: 2024 Matej Zachar
> +//
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Inspired by the wolfssl implementation done by Bastian Germann
> + */
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include "swupdate_crypto.h"
> +#include "swupdate_pkcs11.h"
> +#include "util.h"
> +
> +static CK_SLOT_ID find_slot(CK_FUNCTION_LIST_PTR module, P11KitUri *uri)
> +{
> +	CK_RV rv;
> +
> +	CK_SLOT_ID slot_id = p11_kit_uri_get_slot_id(uri);
> +	if (slot_id != (CK_SLOT_ID)-1)
> +		return slot_id;
> +
> +	size_t slot_count;
> +	rv = module->C_GetSlotList(1, NULL_PTR, &slot_count);
> +	if (rv != CKR_OK)
> +		return (CK_SLOT_ID)-1;
> +
> +	CK_SLOT_ID slot_ids[slot_count];
> +	rv = module->C_GetSlotList(1, &slot_ids[0], &slot_count);
> +	if (rv != CKR_OK)
> +		return (CK_SLOT_ID)-1;
> +
> +	CK_TOKEN_INFO token_info;
> +	for (int i = 0; i < slot_count; ++i) {
> +		slot_id = slot_ids[i];
> +
> +		rv = module->C_GetTokenInfo(slot_id, &token_info);
> +		if (rv != CKR_OK)
> +			return (CK_SLOT_ID)-1;
> +
> +		if (p11_kit_uri_match_token_info(uri, &token_info))
> +			return slot_id;
> +	}
> +
> +	return (CK_SLOT_ID)-1;
> +}
> +
> +static CK_RV find_key(CK_FUNCTION_LIST_PTR module, CK_SESSION_HANDLE session,
> +	CK_ATTRIBUTE_PTR key_id, CK_OBJECT_HANDLE *key_handle)
> +{
> +	CK_RV rv;
> +
> +	CK_ATTRIBUTE find_template[] = {
> +		{ CKA_ID, key_id->pValue, key_id->ulValueLen }
> +	};
> +
> +	rv = module->C_FindObjectsInit(session, find_template, 1);
> +	if (rv != CKR_OK) {
> +		return rv;
> +	}
> +
> +	CK_ULONG object_count;
> +	rv = module->C_FindObjects(session, key_handle, 1, &object_count);
> +	if (rv != CKR_OK) {
> +		return rv;
> +	}
> +
> +	rv = module->C_FindObjectsFinal(session);
> +	if (rv != CKR_OK) {
> +		return rv;
> +	}
> +
> +	if (object_count == 0) {
> +		return CKR_DATA_INVALID;
> +	}
> +
> +	return CKR_OK;
> +}
> +
> +struct pkcs11_digest *pkcs11_DECRYPT_init(unsigned char *uri,
> +	char __attribute__ ((__unused__)) keylen, unsigned char *iv, cipher_t cipher)
> +{
> +	struct pkcs11_digest *dgst;
> +	CK_SLOT_ID slot_id;
> +	CK_ATTRIBUTE_PTR key_id;
> +	const char *pin;
> +	const char *module_path;
> +	const char *msg;
> +	int err = 0;
> +	CK_RV rv;
> +
> +	if (uri == NULL || iv == NULL) {
> +		ERROR("PKCS#11 URI or AES IV missing for decryption!");
> +		return NULL;
> +	}
> +
> +	dgst = calloc(1, sizeof(*dgst));
> +	if (!dgst) {
> +		return NULL;
> +	}
> +
> +	dgst->uri = p11_kit_uri_new();
> +	err = p11_kit_uri_parse((const char*)uri, P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE, dgst->uri);
> +	if (err) {
> +		msg = p11_kit_uri_message(err);
> +		ERROR("PKCS#11 URI: %s", msg);
> +		goto free_digest;
> +	}
> +
> +	key_id	= p11_kit_uri_get_attribute(dgst->uri, CKA_ID);
> +	pin	= p11_kit_uri_get_pin_value(dgst->uri);
> +	module_path = p11_kit_uri_get_module_path(dgst->uri);
> +	if (key_id == NULL || pin == NULL || module_path == NULL) {
> +		ERROR("PKCS#11 URI must contain id, pin-value and module-path.");
> +		goto free_digest;
> +	}
> +
> +	dgst->module = p11_kit_module_load(module_path, 0);
> +	if (dgst->module == NULL) {
> +		msg = p11_kit_message();
> +		ERROR("Failed to load PKCS#11 module [%s]: %s\n", module_path, msg);
> +		goto free_digest;
> +	}
> +
> +	rv = dgst->module->C_Initialize(NULL_PTR);
> +	if (rv != CKR_OK)
> +		goto err_msg;
> +
> +	slot_id = find_slot(dgst->module, dgst->uri);
> +	if (slot_id == -1) {
> +		ERROR("PKCS#11 URI must contain slot-id or token identification such as token, model, serial, manufacturer.");
> +		goto free_digest;
> +	}
> +
> +	rv = dgst->module->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &dgst->session);
> +	if (rv != CKR_OK)
> +		goto err_msg;
> +
> +	rv = dgst->module->C_Login(dgst->session, CKU_USER, (unsigned char *)pin, strnlen(pin, 32));
> +	if (rv != CKR_OK)
> +		goto err_msg;
> +
> +	CK_OBJECT_HANDLE key;
> +	rv = find_key(dgst->module, dgst->session, key_id, &key);
> +	if (rv != CKR_OK)
> +		goto err_msg;
> +
> +	// Setup a valid PKCS#7 block plus one state octet
> +	for (int i = 0; i <= AES_BLK_SIZE; ++i) {
> +		dgst->last[i] = AES_BLK_SIZE;
> +	}
> +
> +	// Setup IV vector & mechanism
> +	memcpy(dgst->iv, iv, AES_BLK_SIZE);
> +	dgst->mechanism.mechanism = CKM_AES_CBC;
> +	dgst->mechanism.pParameter = dgst->iv;
> +	dgst->mechanism.ulParameterLen = AES_BLK_SIZE;
> +
> +	rv = dgst->module->C_DecryptInit(dgst->session, &dgst->mechanism, key);
> +	if (rv != CKR_OK)
> +		goto err_msg;
> +
> +	INFO("PKCS#11 key set up successfully.");
> +	return dgst;
> +
> +err_msg:
> +	msg = p11_kit_strerror(rv);
> +	ERROR("PKCS#11 initialization failed: %s", msg);
> +
> +free_digest:
> +	if (dgst->uri)
> +		p11_kit_uri_free(dgst->uri);
> +
> +	if (dgst->session)
> +		dgst->module->C_CloseSession(dgst->session);
> +
> +	if (dgst->module) {
> +		dgst->module->C_Finalize(NULL_PTR);
> +		p11_kit_module_release(dgst->module);
> +	}
> +
> +	free(dgst);
> +
> +	return NULL;
> +}
> +
> +int pkcs11_DECRYPT_update(struct pkcs11_digest *dgst, unsigned char *buf,
> +	int *outlen, const unsigned char *cryptbuf, int inlen)
> +{
> +	// precondition: len(buf) >= inlen + AES_BLK_SIZE
> +	unsigned long buf_len = inlen + AES_BLK_SIZE;
> +	CK_RV rv;
> +
> +	if (inlen < AES_BLK_SIZE)
> +		return -EFAULT;
> +
> +	if (dgst->last[AES_BLK_SIZE]) {
> +		dgst->last[AES_BLK_SIZE] = 0;
> +		// first run - there is no block to append
> +		*outlen = 0;
> +	} else {
> +		// append previously decrypted last AES block
> +		memcpy(buf, dgst->last, AES_BLK_SIZE);
> +		buf += AES_BLK_SIZE;
> +		*outlen = AES_BLK_SIZE;
> +	}
> +
> +	rv = dgst->module->C_DecryptUpdate(dgst->session, (unsigned char*)cryptbuf, inlen, buf, &buf_len);
> +	if (rv != CKR_OK) {
> +		ERROR("PKCS#11 AES decryption failed: %s", p11_kit_strerror(rv));
> +		return -EFAULT;
> +	}
> +
> +	// strip and remember last AES block from decoded buffer
> +	// it will get appended either in the next call to DECRYPT_update or DECRYPT_final
> +	buf_len -= AES_BLK_SIZE;
> +	memcpy(dgst->last, &buf[buf_len], AES_BLK_SIZE);
> +
> +	// update iv for the next block
> +	memcpy(dgst->iv, cryptbuf + inlen - AES_BLK_SIZE, AES_BLK_SIZE);
> +
> +	*outlen += (int)buf_len;
> +	return 0;
> +}
> +
> +int pkcs11_DECRYPT_final(struct pkcs11_digest *dgst, unsigned char *buf, int *outlen)
> +{
> +	CK_RV rv;
> +	unsigned long extra_len = 0;
> +
> +	if (dgst->last[AES_BLK_SIZE]) {
> +#ifndef CONFIG_ENCRYPTED_IMAGES_HARDEN_LOGGING
> +		ERROR("AES: At least one call to pkcs11_DECRYPT_update was expected");
> +#endif
> +		return -EINVAL;
> +	}
> +
> +	// append previously decrypted last AES block if any
> +	memcpy(buf, dgst->last, AES_BLK_SIZE);
> +
> +	rv = dgst->module->C_DecryptFinal(dgst->session, &buf[AES_BLK_SIZE], &extra_len);
> +	if (rv != CKR_OK)
> +		return -EFAULT;
> +
> +	// obtain last AES block after C_DecryptFinal
> +	CK_BYTE_PTR last = &buf[extra_len];
> +
> +	// Handle manual PKCS#7 padding removal
> +	CK_BYTE padding_value = last[AES_BLK_SIZE - 1];
> +
> +	if (padding_value <= 0 || padding_value > AES_BLK_SIZE) {
> +#ifndef CONFIG_ENCRYPTED_IMAGES_HARDEN_LOGGING
> +		ERROR("AES: Invalid PKCS#7 padding value [%u]", padding_value);
> +#endif
> +		return -EFAULT;
> +	}
> +
> +	// Verify that padding is correct
> +	for (CK_BYTE i = 0; i < padding_value; ++i) {
> +		if (last[AES_BLK_SIZE - 1 - i] != padding_value) {
> +#ifndef CONFIG_ENCRYPTED_IMAGES_HARDEN_LOGGING
> +			ERROR("AES: Invalid PKCS#7 padding value [%u] at offset %u", padding_value, i);
> +#endif
> +			return -EINVAL;
> +		}
> +	}
> +
> +	*outlen = (int)extra_len + AES_BLK_SIZE - padding_value;
> +	return 0;
> +}
> +
> +void pkcs11_DECRYPT_cleanup(struct pkcs11_digest *dgst)
> +{
> +	if (dgst) {
> +		if (dgst->uri)
> +			p11_kit_uri_free(dgst->uri);
> +
> +		if (dgst->session)
> +			dgst->module->C_CloseSession(dgst->session);
> +
> +		if (dgst->module) {
> +			dgst->module->C_Finalize(NULL_PTR);
> +			p11_kit_module_release(dgst->module);
> +		}
> +
> +		free(dgst);
> +		dgst = NULL;
> +	}
> +}
> diff --git a/test/Makefile b/test/Makefile
> index 385efd85..8bdf50d6 100644
> --- a/test/Makefile
> +++ b/test/Makefile
> @@ -17,9 +17,10 @@
>   ## along with this program; if not, write to the Free Software
>   ## Foundation, Inc.
>   
> -ifneq ($(CONFIG_PKCS11),y)
> -tests-$(CONFIG_ENCRYPTED_IMAGES) += test_crypt
> +ifeq ($(CONFIG_PKCS11),y)
> +tests-$(CONFIG_ENCRYPTED_IMAGES) += test_crypt_pkcs11
>   endif
> +tests-$(CONFIG_ENCRYPTED_IMAGES) += test_crypt
>   tests-$(CONFIG_HASH_VERIFY) += test_hash
>   ifeq ($(CONFIG_SIGALG_RAWRSA),y)
>   tests-$(CONFIG_SIGNED_IMAGES) += test_verify
> @@ -66,7 +67,7 @@ quiet_cmd_linktestexe = LD      $(basename $@)
>   						"$(SWLIBS)" \
>   						"$(LDLIBS) cmocka"
>   
> -EXECUTE_TEST = echo "RUN $(subst $(obj)/,,$(var))"; LD_LIBRARY_PATH=$(objtree) CMOCKA_MESSAGE_OUTPUT=TAP $(var)
> +EXECUTE_TEST = echo "RUN $(subst $(obj)/,,$(var))"; LD_LIBRARY_PATH=$(objtree) CMOCKA_MESSAGE_OUTPUT=TAP SOFTHSM2_CONF=$(DATADIR)/token/softhsm.conf $(var)
>   
>   PHONY += default
>   default:
> @@ -111,4 +112,29 @@ $(DATADIR)/signing-secret.pem:
>   	$(if $(Q),@echo "  GEN     $@")
>   	$(Q)openssl genrsa -out $@ 2>/dev/null
>   
> +ifeq ($(CONFIG_PKCS11),y)
> +$(obj)/test_crypt_pkcs11.o: $(DATADIR)/softshm
> +
> +TOKEN_AES_KEY := dd020ce5ebd5c468556288d6a75169c88a5b335d9f569e30751c50401467d230
> +TOKEN_AES_IV := c1f390d21dd06118cbd333144a3318ca
> +
> +.INTERMEDIATE: $(DATADIR)/softshm
> +$(DATADIR)/softshm: export SOFTHSM2_CONF=$(DATADIR)/token/softhsm.conf
> +$(DATADIR)/softshm: $(DATADIR)/token/softhsm.conf
> +	$(if $(Q),@echo "  INIT     $@")
> +	$(Q)rm -rf $(DATADIR)/token/*/
> +
> +	$(if $(Q),@echo "  GEN      $(DATADIR)/token/original.data")
> +	$(Q)openssl rand 131075 > $(DATADIR)/token/original.data
> +
> +	$(if $(Q),@echo "  ENCRYPT  $(DATADIR)/token/original.data")
> +	$(Q)openssl enc -aes-256-cbc -in $(DATADIR)/token/original.data -out $(DATADIR)/token/encrypted.data -K $(TOKEN_AES_KEY) -iv $(TOKEN_AES_IV)
> +	$(Q)echo -n "$(TOKEN_AES_IV)" | xxd -p -r > $(DATADIR)/token/encrypted.data.iv
> +
> +	$(if $(Q),@echo "  IMPORT   $(DATADIR)/token/aes.key")
> +	$(Q)echo -n "$(TOKEN_AES_KEY)" | xxd -p -r > $(DATADIR)/token/aes.key
> +	$(Q)softhsm2-util --init-token --slot 0 --label "TestToken" --so-pin 123456 --pin 1234
> +	$(Q)softhsm2-util --import $(DATADIR)/token/aes.key --aes --token "TestToken" --label "AES key" --id A1B2 --pin 1234
> +endif
> +
>   .PHONY: $(PHONY)
> diff --git a/test/data/token/softhsm.conf b/test/data/token/softhsm.conf
> new file mode 100644
> index 00000000..bd43fe45
> --- /dev/null
> +++ b/test/data/token/softhsm.conf
> @@ -0,0 +1,2 @@
> +directories.tokendir = test/data/token
> +objectstore.backend = file
> \ No newline at end of file
> diff --git a/test/test_crypt_pkcs11.c b/test/test_crypt_pkcs11.c
> new file mode 100644
> index 00000000..94ed92a8
> --- /dev/null
> +++ b/test/test_crypt_pkcs11.c
> @@ -0,0 +1,98 @@
> +// SPDX-FileCopyrightText: 2024 Matej Zachar
> +//
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <stdarg.h>
> +#include <stddef.h>
> +#include <setjmp.h>
> +#include <cmocka.h>
> +#include <util.h>
> +#include "swupdate_crypto.h"
> +
> +#define BUFFER_SIZE (AES_BLK_SIZE * 1024)
> +#define TOKENDIR "test/data/token"
> +
> +static int read_file(const char *path, unsigned char *buffer, size_t *size)
> +{
> +	FILE *fp = fopen(path, "r");
> +	if (!fp) {
> +		fprintf(stderr, "Failed to open file '%s'\n", path);
> +		return -1;
> +	}
> +
> +	size_t len = fread(buffer, sizeof(char), *size, fp);
> +	if (ferror(fp) != 0) {
> +		fprintf(stderr, "Error reading file '%s'\n", path);
> +		fclose(fp);
> +		return -1;
> +	}
> +
> +	*size = len;
> +	fclose(fp);
> +
> +	return 0;
> +}
> +
> +static void test_crypt_pkcs11_256(void **state)
> +{
> +	(void) state;
> +	int err;
> +
> +	const char * uri = "pkcs11:token=TestToken;id=%A1%B2?pin-value=1234&module-path=/usr/lib/softhsm/libsofthsm2.so";
> +
> +	size_t original_data_len = 128 * 1024;/* 128KiB */
> +	unsigned char original_data[original_data_len];
> +	err = read_file(TOKENDIR "/original.data", &original_data[0], &original_data_len);
> +	assert_true(err == 0);
> +
> +	size_t encrypted_data_len = 128 * 1024 + AES_BLK_SIZE;/* 128KiB AES_BLK_SIZE(16B) */
> +	unsigned char encrypted_data[encrypted_data_len];
> +	err = read_file(TOKENDIR "/encrypted.data", &encrypted_data[0], &encrypted_data_len);
> +	assert_true(err == 0);
> +
> +	unsigned char decrypted_data[encrypted_data_len];
> +
> +	size_t iv_size = 16;
> +	unsigned char iv[iv_size];
> +	err = read_file(TOKENDIR "/encrypted.data.iv", &iv[0], &iv_size);
> +	assert_true(err == 0);
> +
> +	unsigned char buffer[BUFFER_SIZE + AES_BLK_SIZE];
> +
> +	struct swupdate_digest *dgst = swupdate_DECRYPT_init((unsigned char *)uri, 0, &iv[0], AES_CBC_256);
> +	assert_non_null(dgst);
> +
> +	int len;
> +	size_t e_offset = 0;
> +	size_t d_offset = 0;
> +	while (e_offset < encrypted_data_len) {
> +		size_t chunk_size = (encrypted_data_len - e_offset > BUFFER_SIZE) ? BUFFER_SIZE : encrypted_data_len - e_offset;
> +
> +		err = swupdate_DECRYPT_update(dgst, buffer, &len, encrypted_data + e_offset, chunk_size);
> +		assert_true(err == 0);
> +		assert_true(len >= AES_BLK_SIZE && len <= chunk_size);
> +		e_offset += chunk_size;
> +
> +		memcpy(&decrypted_data[d_offset], buffer, len);
> +		d_offset += len;
> +	}
> +
> +	err = swupdate_DECRYPT_final(dgst, buffer, &len);
> +	assert_true(err == 0);
> +	assert_true(len == 3); /* as the size is 128*1024+3 */
> +
> +	memcpy(&decrypted_data[d_offset], buffer, len);
> +	d_offset += len;
> +
> +	assert_true(strncmp((const char *)decrypted_data, (const char *)original_data, original_data_len) == 0);
> +}
> +
> +int main(void)
> +{
> +	const struct CMUnitTest crypt_pkcs11_tests[] = {
> +		cmocka_unit_test(test_crypt_pkcs11_256)
> +	};
> +	return cmocka_run_group_tests_name("crypt_pkcs11", crypt_pkcs11_tests, NULL, NULL);
> +}
>
diff mbox series

Patch

diff --git a/Makefile.flags b/Makefile.flags
index 65f112e8..40dd3b66 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -163,8 +163,6 @@  endif
 ifeq ($(CONFIG_SSL_IMPL_WOLFSSL),y)
 KBUILD_CPPFLAGS += -DOPENSSL_ALL
 LDLIBS += wolfssl
-else ifeq ($(CONFIG_PKCS11),y)
-LDLIBS += wolfssl
 endif
 
 ifeq ($(CONFIG_SSL_IMPL_MBEDTLS),y)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 4b9db821..48eeb01c 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -21,6 +21,10 @@  menu "Crypto libraries"
 	config SSL_IMPL_GPGME
 		bool "gpgme"
 		depends on HAVE_GPGME
+
+	config PKCS11
+		bool "PKCS#11 (p11-kit)"
+		depends on HAVE_P11KIT
 endmenu
 
 config HASH_VERIFY
@@ -82,9 +86,9 @@  menu "Encryption"
 
 config ENCRYPTED_IMAGES
 	bool "Images can be encrypted with a symmetric key"
-	depends on SSL_IMPL_OPENSSL || SSL_IMPL_WOLFSSL || SSL_IMPL_MBEDTLS
+	depends on SSL_IMPL_OPENSSL || SSL_IMPL_WOLFSSL || SSL_IMPL_MBEDTLS || PKCS11
 comment "Image encryption needs an SSL implementation"
-	depends on !SSL_IMPL_OPENSSL && !SSL_IMPL_WOLFSSL && !SSL_IMPL_MBEDTLS
+	depends on !SSL_IMPL_OPENSSL && !SSL_IMPL_WOLFSSL && !SSL_IMPL_MBEDTLS && !PKCS11
 
 config ENCRYPTED_SW_DESCRIPTION
 	bool "Even sw-description is encrypted"
@@ -126,13 +130,5 @@  config ENCRYPTED_IMAGES_HARDEN_LOGGING
 	  hash mismatch and errors in the decryption finalization (padding) of a
 	  streamed image are suppressed.
 
-config PKCS11
-	bool "Enable PKCS#11 cryptographic operations"
-	default n
-	depends on SSL_IMPL_WOLFSSL && HAVE_P11KIT && ENCRYPTED_IMAGES
-	help
-	  Enable using PKCS#11 for AES decryption instead of having the plain
-	  key available in a file. This is implemented with wolfSSL independent
-	  from the SSL implementation and replaces the plain key method.
 endmenu
 
diff --git a/crypto/Makefile b/crypto/Makefile
index 25ab3ab7..b591ff57 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -14,10 +14,8 @@  endif
 ifeq ($(CONFIG_SSL_IMPL_WOLFSSL),y)
 obj-$(CONFIG_HASH_VERIFY)	+= swupdate_HASH_wolfssl.o
 obj-$(CONFIG_SIGALG_CMS)	+= swupdate_pkcs7_verify_wolfssl.o
-ifeq ($(CONFIG_PKCS11),y)
 obj-$(CONFIG_ENCRYPTED_IMAGES)	+= swupdate_decrypt_wolfssl.o
 endif
-endif
 
 ifeq ($(CONFIG_SSL_IMPL_MBEDTLS),y)
 obj-$(CONFIG_HASH_VERIFY)	+= swupdate_HASH_mbedtls.o
@@ -28,3 +26,7 @@  endif
 ifeq ($(CONFIG_SSL_IMPL_GPGME),y)
 obj-$(CONFIG_SIGALG_GPG)	+= swupdate_gpg_verify.o
 endif
+
+ifeq ($(CONFIG_PKCS11),y)
+obj-$(CONFIG_ENCRYPTED_IMAGES)	+= swupdate_decrypt_pkcs11_p11kit.o
+endif
diff --git a/crypto/swupdate_decrypt_pkcs11_p11kit.c b/crypto/swupdate_decrypt_pkcs11_p11kit.c
new file mode 100644
index 00000000..f66426bc
--- /dev/null
+++ b/crypto/swupdate_decrypt_pkcs11_p11kit.c
@@ -0,0 +1,290 @@ 
+// SPDX-FileCopyrightText: 2024 Matej Zachar
+//
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Inspired by the wolfssl implementation done by Bastian Germann
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "swupdate_crypto.h"
+#include "swupdate_pkcs11.h"
+#include "util.h"
+
+static CK_SLOT_ID find_slot(CK_FUNCTION_LIST_PTR module, P11KitUri *uri)
+{
+	CK_RV rv;
+
+	CK_SLOT_ID slot_id = p11_kit_uri_get_slot_id(uri);
+	if (slot_id != (CK_SLOT_ID)-1)
+		return slot_id;
+
+	size_t slot_count;
+	rv = module->C_GetSlotList(1, NULL_PTR, &slot_count);
+	if (rv != CKR_OK)
+		return (CK_SLOT_ID)-1;
+
+	CK_SLOT_ID slot_ids[slot_count];
+	rv = module->C_GetSlotList(1, &slot_ids[0], &slot_count);
+	if (rv != CKR_OK)
+		return (CK_SLOT_ID)-1;
+
+	CK_TOKEN_INFO token_info;
+	for (int i = 0; i < slot_count; ++i) {
+		slot_id = slot_ids[i];
+
+		rv = module->C_GetTokenInfo(slot_id, &token_info);
+		if (rv != CKR_OK)
+			return (CK_SLOT_ID)-1;
+
+		if (p11_kit_uri_match_token_info(uri, &token_info))
+			return slot_id;
+	}
+
+	return (CK_SLOT_ID)-1;
+}
+
+static CK_RV find_key(CK_FUNCTION_LIST_PTR module, CK_SESSION_HANDLE session,
+	CK_ATTRIBUTE_PTR key_id, CK_OBJECT_HANDLE *key_handle)
+{
+	CK_RV rv;
+
+	CK_ATTRIBUTE find_template[] = {
+		{ CKA_ID, key_id->pValue, key_id->ulValueLen }
+	};
+
+	rv = module->C_FindObjectsInit(session, find_template, 1);
+	if (rv != CKR_OK) {
+		return rv;
+	}
+
+	CK_ULONG object_count;
+	rv = module->C_FindObjects(session, key_handle, 1, &object_count);
+	if (rv != CKR_OK) {
+		return rv;
+	}
+
+	rv = module->C_FindObjectsFinal(session);
+	if (rv != CKR_OK) {
+		return rv;
+	}
+
+	if (object_count == 0) {
+		return CKR_DATA_INVALID;
+	}
+
+	return CKR_OK;
+}
+
+struct pkcs11_digest *pkcs11_DECRYPT_init(unsigned char *uri,
+	char __attribute__ ((__unused__)) keylen, unsigned char *iv, cipher_t cipher)
+{
+	struct pkcs11_digest *dgst;
+	CK_SLOT_ID slot_id;
+	CK_ATTRIBUTE_PTR key_id;
+	const char *pin;
+	const char *module_path;
+	const char *msg;
+	int err = 0;
+	CK_RV rv;
+
+	if (uri == NULL || iv == NULL) {
+		ERROR("PKCS#11 URI or AES IV missing for decryption!");
+		return NULL;
+	}
+
+	dgst = calloc(1, sizeof(*dgst));
+	if (!dgst) {
+		return NULL;
+	}
+
+	dgst->uri = p11_kit_uri_new();
+	err = p11_kit_uri_parse((const char*)uri, P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE, dgst->uri);
+	if (err) {
+		msg = p11_kit_uri_message(err);
+		ERROR("PKCS#11 URI: %s", msg);
+		goto free_digest;
+	}
+
+	key_id	= p11_kit_uri_get_attribute(dgst->uri, CKA_ID);
+	pin	= p11_kit_uri_get_pin_value(dgst->uri);
+	module_path = p11_kit_uri_get_module_path(dgst->uri);
+	if (key_id == NULL || pin == NULL || module_path == NULL) {
+		ERROR("PKCS#11 URI must contain id, pin-value and module-path.");
+		goto free_digest;
+	}
+
+	dgst->module = p11_kit_module_load(module_path, 0);
+	if (dgst->module == NULL) {
+		msg = p11_kit_message();
+		ERROR("Failed to load PKCS#11 module [%s]: %s\n", module_path, msg);
+		goto free_digest;
+	}
+
+	rv = dgst->module->C_Initialize(NULL_PTR);
+	if (rv != CKR_OK)
+		goto err_msg;
+
+	slot_id = find_slot(dgst->module, dgst->uri);
+	if (slot_id == -1) {
+		ERROR("PKCS#11 URI must contain slot-id or token identification such as token, model, serial, manufacturer.");
+		goto free_digest;
+	}
+
+	rv = dgst->module->C_OpenSession(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &dgst->session);
+	if (rv != CKR_OK)
+		goto err_msg;
+
+	rv = dgst->module->C_Login(dgst->session, CKU_USER, (unsigned char *)pin, strnlen(pin, 32));
+	if (rv != CKR_OK)
+		goto err_msg;
+
+	CK_OBJECT_HANDLE key;
+	rv = find_key(dgst->module, dgst->session, key_id, &key);
+	if (rv != CKR_OK)
+		goto err_msg;
+
+	// Setup a valid PKCS#7 block plus one state octet
+	for (int i = 0; i <= AES_BLK_SIZE; ++i) {
+		dgst->last[i] = AES_BLK_SIZE;
+	}
+
+	// Setup IV vector & mechanism
+	memcpy(dgst->iv, iv, AES_BLK_SIZE);
+	dgst->mechanism.mechanism = CKM_AES_CBC;
+	dgst->mechanism.pParameter = dgst->iv;
+	dgst->mechanism.ulParameterLen = AES_BLK_SIZE;
+
+	rv = dgst->module->C_DecryptInit(dgst->session, &dgst->mechanism, key);
+	if (rv != CKR_OK)
+		goto err_msg;
+
+	INFO("PKCS#11 key set up successfully.");
+	return dgst;
+
+err_msg:
+	msg = p11_kit_strerror(rv);
+	ERROR("PKCS#11 initialization failed: %s", msg);
+
+free_digest:
+	if (dgst->uri)
+		p11_kit_uri_free(dgst->uri);
+
+	if (dgst->session)
+		dgst->module->C_CloseSession(dgst->session);
+
+	if (dgst->module) {
+		dgst->module->C_Finalize(NULL_PTR);
+		p11_kit_module_release(dgst->module);
+	}
+
+	free(dgst);
+
+	return NULL;
+}
+
+int pkcs11_DECRYPT_update(struct pkcs11_digest *dgst, unsigned char *buf,
+	int *outlen, const unsigned char *cryptbuf, int inlen)
+{
+	// precondition: len(buf) >= inlen + AES_BLK_SIZE
+	unsigned long buf_len = inlen + AES_BLK_SIZE;
+	CK_RV rv;
+
+	if (inlen < AES_BLK_SIZE)
+		return -EFAULT;
+
+	if (dgst->last[AES_BLK_SIZE]) {
+		dgst->last[AES_BLK_SIZE] = 0;
+		// first run - there is no block to append
+		*outlen = 0;
+	} else {
+		// append previously decrypted last AES block
+		memcpy(buf, dgst->last, AES_BLK_SIZE);
+		buf += AES_BLK_SIZE;
+		*outlen = AES_BLK_SIZE;
+	}
+
+	rv = dgst->module->C_DecryptUpdate(dgst->session, (unsigned char*)cryptbuf, inlen, buf, &buf_len);
+	if (rv != CKR_OK) {
+		ERROR("PKCS#11 AES decryption failed: %s", p11_kit_strerror(rv));
+		return -EFAULT;
+	}
+
+	// strip and remember last AES block from decoded buffer
+	// it will get appended either in the next call to DECRYPT_update or DECRYPT_final
+	buf_len -= AES_BLK_SIZE;
+	memcpy(dgst->last, &buf[buf_len], AES_BLK_SIZE);
+
+	// update iv for the next block
+	memcpy(dgst->iv, cryptbuf + inlen - AES_BLK_SIZE, AES_BLK_SIZE);
+
+	*outlen += (int)buf_len;
+	return 0;
+}
+
+int pkcs11_DECRYPT_final(struct pkcs11_digest *dgst, unsigned char *buf, int *outlen)
+{
+	CK_RV rv;
+	unsigned long extra_len = 0;
+
+	if (dgst->last[AES_BLK_SIZE]) {
+#ifndef CONFIG_ENCRYPTED_IMAGES_HARDEN_LOGGING
+		ERROR("AES: At least one call to pkcs11_DECRYPT_update was expected");
+#endif
+		return -EINVAL;
+	}
+
+	// append previously decrypted last AES block if any
+	memcpy(buf, dgst->last, AES_BLK_SIZE);
+
+	rv = dgst->module->C_DecryptFinal(dgst->session, &buf[AES_BLK_SIZE], &extra_len);
+	if (rv != CKR_OK)
+		return -EFAULT;
+
+	// obtain last AES block after C_DecryptFinal
+	CK_BYTE_PTR last = &buf[extra_len];
+
+	// Handle manual PKCS#7 padding removal
+	CK_BYTE padding_value = last[AES_BLK_SIZE - 1];
+
+	if (padding_value <= 0 || padding_value > AES_BLK_SIZE) {
+#ifndef CONFIG_ENCRYPTED_IMAGES_HARDEN_LOGGING
+		ERROR("AES: Invalid PKCS#7 padding value [%u]", padding_value);
+#endif
+		return -EFAULT;
+	}
+
+	// Verify that padding is correct
+	for (CK_BYTE i = 0; i < padding_value; ++i) {
+		if (last[AES_BLK_SIZE - 1 - i] != padding_value) {
+#ifndef CONFIG_ENCRYPTED_IMAGES_HARDEN_LOGGING
+			ERROR("AES: Invalid PKCS#7 padding value [%u] at offset %u", padding_value, i);
+#endif
+			return -EINVAL;
+		}
+	}
+
+	*outlen = (int)extra_len + AES_BLK_SIZE - padding_value;
+	return 0;
+}
+
+void pkcs11_DECRYPT_cleanup(struct pkcs11_digest *dgst)
+{
+	if (dgst) {
+		if (dgst->uri)
+			p11_kit_uri_free(dgst->uri);
+
+		if (dgst->session)
+			dgst->module->C_CloseSession(dgst->session);
+
+		if (dgst->module) {
+			dgst->module->C_Finalize(NULL_PTR);
+			p11_kit_module_release(dgst->module);
+		}
+
+		free(dgst);
+		dgst = NULL;
+	}
+}
diff --git a/test/Makefile b/test/Makefile
index 385efd85..8bdf50d6 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -17,9 +17,10 @@ 
 ## along with this program; if not, write to the Free Software
 ## Foundation, Inc.
 
-ifneq ($(CONFIG_PKCS11),y)
-tests-$(CONFIG_ENCRYPTED_IMAGES) += test_crypt
+ifeq ($(CONFIG_PKCS11),y)
+tests-$(CONFIG_ENCRYPTED_IMAGES) += test_crypt_pkcs11
 endif
+tests-$(CONFIG_ENCRYPTED_IMAGES) += test_crypt
 tests-$(CONFIG_HASH_VERIFY) += test_hash
 ifeq ($(CONFIG_SIGALG_RAWRSA),y)
 tests-$(CONFIG_SIGNED_IMAGES) += test_verify
@@ -66,7 +67,7 @@  quiet_cmd_linktestexe = LD      $(basename $@)
 						"$(SWLIBS)" \
 						"$(LDLIBS) cmocka"
 
-EXECUTE_TEST = echo "RUN $(subst $(obj)/,,$(var))"; LD_LIBRARY_PATH=$(objtree) CMOCKA_MESSAGE_OUTPUT=TAP $(var)
+EXECUTE_TEST = echo "RUN $(subst $(obj)/,,$(var))"; LD_LIBRARY_PATH=$(objtree) CMOCKA_MESSAGE_OUTPUT=TAP SOFTHSM2_CONF=$(DATADIR)/token/softhsm.conf $(var)
 
 PHONY += default
 default:
@@ -111,4 +112,29 @@  $(DATADIR)/signing-secret.pem:
 	$(if $(Q),@echo "  GEN     $@")
 	$(Q)openssl genrsa -out $@ 2>/dev/null
 
+ifeq ($(CONFIG_PKCS11),y)
+$(obj)/test_crypt_pkcs11.o: $(DATADIR)/softshm
+
+TOKEN_AES_KEY := dd020ce5ebd5c468556288d6a75169c88a5b335d9f569e30751c50401467d230
+TOKEN_AES_IV := c1f390d21dd06118cbd333144a3318ca
+
+.INTERMEDIATE: $(DATADIR)/softshm
+$(DATADIR)/softshm: export SOFTHSM2_CONF=$(DATADIR)/token/softhsm.conf
+$(DATADIR)/softshm: $(DATADIR)/token/softhsm.conf
+	$(if $(Q),@echo "  INIT     $@")
+	$(Q)rm -rf $(DATADIR)/token/*/
+
+	$(if $(Q),@echo "  GEN      $(DATADIR)/token/original.data")
+	$(Q)openssl rand 131075 > $(DATADIR)/token/original.data
+
+	$(if $(Q),@echo "  ENCRYPT  $(DATADIR)/token/original.data")
+	$(Q)openssl enc -aes-256-cbc -in $(DATADIR)/token/original.data -out $(DATADIR)/token/encrypted.data -K $(TOKEN_AES_KEY) -iv $(TOKEN_AES_IV)
+	$(Q)echo -n "$(TOKEN_AES_IV)" | xxd -p -r > $(DATADIR)/token/encrypted.data.iv
+
+	$(if $(Q),@echo "  IMPORT   $(DATADIR)/token/aes.key")
+	$(Q)echo -n "$(TOKEN_AES_KEY)" | xxd -p -r > $(DATADIR)/token/aes.key
+	$(Q)softhsm2-util --init-token --slot 0 --label "TestToken" --so-pin 123456 --pin 1234
+	$(Q)softhsm2-util --import $(DATADIR)/token/aes.key --aes --token "TestToken" --label "AES key" --id A1B2 --pin 1234
+endif
+
 .PHONY: $(PHONY)
diff --git a/test/data/token/softhsm.conf b/test/data/token/softhsm.conf
new file mode 100644
index 00000000..bd43fe45
--- /dev/null
+++ b/test/data/token/softhsm.conf
@@ -0,0 +1,2 @@ 
+directories.tokendir = test/data/token
+objectstore.backend = file
\ No newline at end of file
diff --git a/test/test_crypt_pkcs11.c b/test/test_crypt_pkcs11.c
new file mode 100644
index 00000000..94ed92a8
--- /dev/null
+++ b/test/test_crypt_pkcs11.c
@@ -0,0 +1,98 @@ 
+// SPDX-FileCopyrightText: 2024 Matej Zachar
+//
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <util.h>
+#include "swupdate_crypto.h"
+
+#define BUFFER_SIZE (AES_BLK_SIZE * 1024)
+#define TOKENDIR "test/data/token"
+
+static int read_file(const char *path, unsigned char *buffer, size_t *size)
+{
+	FILE *fp = fopen(path, "r");
+	if (!fp) {
+		fprintf(stderr, "Failed to open file '%s'\n", path);
+		return -1;
+	}
+
+	size_t len = fread(buffer, sizeof(char), *size, fp);
+	if (ferror(fp) != 0) {
+		fprintf(stderr, "Error reading file '%s'\n", path);
+		fclose(fp);
+		return -1;
+	}
+
+	*size = len;
+	fclose(fp);
+
+	return 0;
+}
+
+static void test_crypt_pkcs11_256(void **state)
+{
+	(void) state;
+	int err;
+
+	const char * uri = "pkcs11:token=TestToken;id=%A1%B2?pin-value=1234&module-path=/usr/lib/softhsm/libsofthsm2.so";
+
+	size_t original_data_len = 128 * 1024;/* 128KiB */
+	unsigned char original_data[original_data_len];
+	err = read_file(TOKENDIR "/original.data", &original_data[0], &original_data_len);
+	assert_true(err == 0);
+
+	size_t encrypted_data_len = 128 * 1024 + AES_BLK_SIZE;/* 128KiB AES_BLK_SIZE(16B) */
+	unsigned char encrypted_data[encrypted_data_len];
+	err = read_file(TOKENDIR "/encrypted.data", &encrypted_data[0], &encrypted_data_len);
+	assert_true(err == 0);
+
+	unsigned char decrypted_data[encrypted_data_len];
+
+	size_t iv_size = 16;
+	unsigned char iv[iv_size];
+	err = read_file(TOKENDIR "/encrypted.data.iv", &iv[0], &iv_size);
+	assert_true(err == 0);
+
+	unsigned char buffer[BUFFER_SIZE + AES_BLK_SIZE];
+
+	struct swupdate_digest *dgst = swupdate_DECRYPT_init((unsigned char *)uri, 0, &iv[0], AES_CBC_256);
+	assert_non_null(dgst);
+
+	int len;
+	size_t e_offset = 0;
+	size_t d_offset = 0;
+	while (e_offset < encrypted_data_len) {
+		size_t chunk_size = (encrypted_data_len - e_offset > BUFFER_SIZE) ? BUFFER_SIZE : encrypted_data_len - e_offset;
+
+		err = swupdate_DECRYPT_update(dgst, buffer, &len, encrypted_data + e_offset, chunk_size);
+		assert_true(err == 0);
+		assert_true(len >= AES_BLK_SIZE && len <= chunk_size);
+		e_offset += chunk_size;
+
+		memcpy(&decrypted_data[d_offset], buffer, len);
+		d_offset += len;
+	}
+
+	err = swupdate_DECRYPT_final(dgst, buffer, &len);
+	assert_true(err == 0);
+	assert_true(len == 3); /* as the size is 128*1024+3 */
+
+	memcpy(&decrypted_data[d_offset], buffer, len);
+	d_offset += len;
+
+	assert_true(strncmp((const char *)decrypted_data, (const char *)original_data, original_data_len) == 0);
+}
+
+int main(void)
+{
+	const struct CMUnitTest crypt_pkcs11_tests[] = {
+		cmocka_unit_test(test_crypt_pkcs11_256)
+	};
+	return cmocka_run_group_tests_name("crypt_pkcs11", crypt_pkcs11_tests, NULL, NULL);
+}