mkimage: Add support for signing with pkcs11

Add support for signing with the pkcs11 engine. This allows FIT images
to be signed with keys securely stored on a smartcard, hardware security
module, etc without exposing the keys.

Support for other engines can be added in the future by modifying
rsa_engine_get_pub_key() and rsa_engine_get_priv_key() to construct
correct key_id strings.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt
index e487401..7cdb7bf 100644
--- a/doc/uImage.FIT/signature.txt
+++ b/doc/uImage.FIT/signature.txt
@@ -385,6 +385,149 @@
 Test passed
 
 
+Hardware Signing with PKCS#11
+-----------------------------
+
+Securely managing private signing keys can challenging, especially when the
+keys are stored on the file system of a computer that is connected to the
+Internet. If an attacker is able to steal the key, they can sign malicious FIT
+images which will appear genuine to your devices.
+
+An alternative solution is to keep your signing key securely stored on hardware
+device like a smartcard, USB token or Hardware Security Module (HSM) and have
+them perform the signing. PKCS#11 is standard for interfacing with these crypto
+device.
+
+Requirements:
+Smartcard/USB token/HSM which can work with the pkcs11 engine
+openssl
+libp11 (provides pkcs11 engine)
+p11-kit (recommended to simplify setup)
+opensc (for smartcards and smartcard like USB devices)
+gnutls (recommended for key generation, p11tool)
+
+The following examples use the Nitrokey Pro. Instructions for other devices may vary.
+
+Notes on pkcs11 engine setup:
+
+Make sure p11-kit, opensc are installed and that p11-kit is setup to use opensc.
+/usr/share/p11-kit/modules/opensc.module should be present on your system.
+
+
+Generating Keys On the Nitrokey:
+
+$ gpg --card-edit
+
+Reader ...........: Nitrokey Nitrokey Pro (xxxxxxxx0000000000000000) 00 00
+Application ID ...: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+Version ..........: 2.1
+Manufacturer .....: ZeitControl
+Serial number ....: xxxxxxxx
+Name of cardholder: [not set]
+Language prefs ...: de
+Sex ..............: unspecified
+URL of public key : [not set]
+Login data .......: [not set]
+Signature PIN ....: forced
+Key attributes ...: rsa2048 rsa2048 rsa2048
+Max. PIN lengths .: 32 32 32
+PIN retry counter : 3 0 3
+Signature counter : 0
+Signature key ....: [none]
+Encryption key....: [none]
+Authentication key: [none]
+General key info..: [none]
+
+gpg/card> generate
+Make off-card backup of encryption key? (Y/n) n
+
+Please note that the factory settings of the PINs are
+  PIN = '123456' Admin PIN = '12345678'
+You should change them using the command --change-pin
+
+What keysize do you want for the Signature key? (2048) 4096
+The card will now be re-configured to generate a key of 4096 bits
+Note: There is no guarantee that the card supports the requested size.
+  If the key generation does not succeed, please check the
+  documentation of your card to see what sizes are allowed.
+What keysize do you want for the Encryption key? (2048) 4096
+The card will now be re-configured to generate a key of 4096 bits
+What keysize do you want for the Authentication key? (2048) 4096
+The card will now be re-configured to generate a key of 4096 bits
+Please specify how long the key should be valid.
+  0 = key does not expire
+  <n> = key expires in n days
+  <n>w = key expires in n weeks
+  <n>m = key expires in n months
+  <n>y = key expires in n years
+Key is valid for? (0)
+Key does not expire at all
+Is this correct? (y/N) y
+
+GnuPG needs to construct a user ID to identify your key.
+
+Real name: John Doe
+Email address: john.doe@email.com
+Comment:
+You selected this USER-ID:
+  "John Doe <john.doe@email.com>"
+
+Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
+
+
+Using p11tool to get the token URL:
+
+Depending on system configuration, gpg-agent may need to be killed first.
+
+$ p11tool --provider /usr/lib/opensc-pkcs11.so --list-tokens
+Token 0:
+URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29
+Label: OpenPGP card (User PIN (sig))
+Type: Hardware token
+Manufacturer: ZeitControl
+Model: PKCS#15 emulated
+Serial: 000xxxxxxxxx
+Module: (null)
+
+
+Token 1:
+URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%29
+Label: OpenPGP card (User PIN)
+Type: Hardware token
+Manufacturer: ZeitControl
+Model: PKCS#15 emulated
+Serial: 000xxxxxxxxx
+Module: (null)
+
+Use the portion of the signature token URL after "pkcs11:" as the keydir argument (-k) to mkimage below.
+
+
+Use the URL of the token to list the private keys:
+
+$ p11tool --login --provider /usr/lib/opensc-pkcs11.so --list-privkeys \
+"pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29"
+Token 'OpenPGP card (User PIN (sig))' with URL 'pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29' requires user PIN
+Enter PIN:
+Object 0:
+URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29;id=%01;object=Signature%20key;type=private
+Type: Private key
+Label: Signature key
+Flags: CKA_PRIVATE; CKA_NEVER_EXTRACTABLE; CKA_SENSITIVE;
+ID: 01
+
+Use the label, in this case "Signature key" as the key-name-hint in your FIT.
+
+Create the fitImage:
+$ ./tools/mkimage -f fit-image.its fitImage
+
+
+Sign the fitImage with the hardware key:
+
+$ ./tools/mkimage -F -k \
+"model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29" \
+-K u-boot.dtb -N pkcs11 -r fitImage
+
+
 Future Work
 -----------
 - Roll-back protection using a TPM is done using the tpm command. This can
diff --git a/include/image.h b/include/image.h
index 0537678..6207d62 100644
--- a/include/image.h
+++ b/include/image.h
@@ -965,6 +965,7 @@
  * @fit:	Pointer to the FIT format image header
  * @comment:	Comment to add to signature nodes
  * @require_keys: Mark all keys as 'required'
+ * @engine_id:	Engine to use for signing
  *
  * Adds hash values for all component images in the FIT blob.
  * Hashes are calculated for all component images which have hash subnodes
@@ -977,7 +978,8 @@
  *     libfdt error code, on failure
  */
 int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
-			      const char *comment, int require_keys);
+			      const char *comment, int require_keys,
+			      const char *engine_id);
 
 int fit_image_verify(const void *fit, int noffset);
 int fit_config_verify(const void *fit, int conf_noffset);
@@ -1057,6 +1059,7 @@
 	const void *fdt_blob;		/* FDT containing public keys */
 	int required_keynode;		/* Node offset of key to use: -1=any */
 	const char *require_keys;	/* Value for 'required' property */
+	const char *engine_id;		/* Engine to use for signing */
 };
 #endif /* Allow struct image_region to always be defined for rsa.h */
 
diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c
index 9a09280..8c6637e 100644
--- a/lib/rsa/rsa-sign.c
+++ b/lib/rsa/rsa-sign.c
@@ -14,6 +14,7 @@
 #include <openssl/err.h>
 #include <openssl/ssl.h>
 #include <openssl/evp.h>
+#include <openssl/engine.h>
 
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
 #define HAVE_ERR_REMOVE_THREAD_STATE
@@ -31,14 +32,14 @@
 }
 
 /**
- * rsa_get_pub_key() - read a public key from a .crt file
+ * rsa_pem_get_pub_key() - read a public key from a .crt file
  *
  * @keydir:	Directory containins the key
  * @name	Name of key file (will have a .crt extension)
  * @rsap	Returns RSA object, or NULL on failure
  * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
  */
-static int rsa_get_pub_key(const char *keydir, const char *name, RSA **rsap)
+static int rsa_pem_get_pub_key(const char *keydir, const char *name, RSA **rsap)
 {
 	char path[1024];
 	EVP_PKEY *key;
@@ -96,14 +97,90 @@
 }
 
 /**
- * rsa_get_priv_key() - read a private key from a .key file
+ * rsa_engine_get_pub_key() - read a public key from given engine
  *
- * @keydir:	Directory containins the key
+ * @keydir:	Key prefix
+ * @name	Name of key
+ * @engine	Engine to use
+ * @rsap	Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_engine_get_pub_key(const char *keydir, const char *name,
+				  ENGINE *engine, RSA **rsap)
+{
+	const char *engine_id;
+	char key_id[1024];
+	EVP_PKEY *key;
+	RSA *rsa;
+	int ret;
+
+	*rsap = NULL;
+
+	engine_id = ENGINE_get_id(engine);
+
+	if (engine_id && !strcmp(engine_id, "pkcs11")) {
+		if (keydir)
+			snprintf(key_id, sizeof(key_id),
+				 "pkcs11:%s;object=%s;type=public",
+				 keydir, name);
+		else
+			snprintf(key_id, sizeof(key_id),
+				 "pkcs11:object=%s;type=public",
+				 name);
+	} else {
+		fprintf(stderr, "Engine not supported\n");
+		return -ENOTSUP;
+	}
+
+	key = ENGINE_load_public_key(engine, key_id, NULL, NULL);
+	if (!key)
+		return rsa_err("Failure loading public key from engine");
+
+	/* Convert to a RSA_style key. */
+	rsa = EVP_PKEY_get1_RSA(key);
+	if (!rsa) {
+		rsa_err("Couldn't convert to a RSA style key");
+		ret = -EINVAL;
+		goto err_rsa;
+	}
+
+	EVP_PKEY_free(key);
+	*rsap = rsa;
+
+	return 0;
+
+err_rsa:
+	EVP_PKEY_free(key);
+	return ret;
+}
+
+/**
+ * rsa_get_pub_key() - read a public key
+ *
+ * @keydir:	Directory containing the key (PEM file) or key prefix (engine)
+ * @name	Name of key file (will have a .crt extension)
+ * @engine	Engine to use
+ * @rsap	Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_get_pub_key(const char *keydir, const char *name,
+			   ENGINE *engine, RSA **rsap)
+{
+	if (engine)
+		return rsa_engine_get_pub_key(keydir, name, engine, rsap);
+	return rsa_pem_get_pub_key(keydir, name, rsap);
+}
+
+/**
+ * rsa_pem_get_priv_key() - read a private key from a .key file
+ *
+ * @keydir:	Directory containing the key
  * @name	Name of key file (will have a .key extension)
  * @rsap	Returns RSA object, or NULL on failure
  * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
  */
-static int rsa_get_priv_key(const char *keydir, const char *name, RSA **rsap)
+static int rsa_pem_get_priv_key(const char *keydir, const char *name,
+				RSA **rsap)
 {
 	char path[1024];
 	RSA *rsa;
@@ -130,6 +207,81 @@
 	return 0;
 }
 
+/**
+ * rsa_engine_get_priv_key() - read a private key from given engine
+ *
+ * @keydir:	Key prefix
+ * @name	Name of key
+ * @engine	Engine to use
+ * @rsap	Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_engine_get_priv_key(const char *keydir, const char *name,
+				   ENGINE *engine, RSA **rsap)
+{
+	const char *engine_id;
+	char key_id[1024];
+	EVP_PKEY *key;
+	RSA *rsa;
+	int ret;
+
+	*rsap = NULL;
+
+	engine_id = ENGINE_get_id(engine);
+
+	if (engine_id && !strcmp(engine_id, "pkcs11")) {
+		if (keydir)
+			snprintf(key_id, sizeof(key_id),
+				 "pkcs11:%s;object=%s;type=private",
+				 keydir, name);
+		else
+			snprintf(key_id, sizeof(key_id),
+				 "pkcs11:object=%s;type=private",
+				 name);
+	} else {
+		fprintf(stderr, "Engine not supported\n");
+		return -ENOTSUP;
+	}
+
+	key = ENGINE_load_private_key(engine, key_id, NULL, NULL);
+	if (!key)
+		return rsa_err("Failure loading private key from engine");
+
+	/* Convert to a RSA_style key. */
+	rsa = EVP_PKEY_get1_RSA(key);
+	if (!rsa) {
+		rsa_err("Couldn't convert to a RSA style key");
+		ret = -EINVAL;
+		goto err_rsa;
+	}
+
+	EVP_PKEY_free(key);
+	*rsap = rsa;
+
+	return 0;
+
+err_rsa:
+	EVP_PKEY_free(key);
+	return ret;
+}
+
+/**
+ * rsa_get_priv_key() - read a private key
+ *
+ * @keydir:	Directory containing the key (PEM file) or key prefix (engine)
+ * @name	Name of key
+ * @engine	Engine to use for signing
+ * @rsap	Returns RSA object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ */
+static int rsa_get_priv_key(const char *keydir, const char *name,
+			    ENGINE *engine, RSA **rsap)
+{
+	if (engine)
+		return rsa_engine_get_priv_key(keydir, name, engine, rsap);
+	return rsa_pem_get_priv_key(keydir, name, rsap);
+}
+
 static int rsa_init(void)
 {
 	int ret;
@@ -148,6 +300,45 @@
 	return 0;
 }
 
+static int rsa_engine_init(const char *engine_id, ENGINE **pe)
+{
+	ENGINE *e;
+	int ret;
+
+	ENGINE_load_builtin_engines();
+
+	e = ENGINE_by_id(engine_id);
+	if (!e) {
+		fprintf(stderr, "Engine isn't available\n");
+		ret = -1;
+		goto err_engine_by_id;
+	}
+
+	if (!ENGINE_init(e)) {
+		fprintf(stderr, "Couldn't initialize engine\n");
+		ret = -1;
+		goto err_engine_init;
+	}
+
+	if (!ENGINE_set_default_RSA(e)) {
+		fprintf(stderr, "Couldn't set engine as default for RSA\n");
+		ret = -1;
+		goto err_set_rsa;
+	}
+
+	*pe = e;
+
+	return 0;
+
+err_set_rsa:
+	ENGINE_finish(e);
+err_engine_init:
+	ENGINE_free(e);
+err_engine_by_id:
+	ENGINE_cleanup();
+	return ret;
+}
+
 static void rsa_remove(void)
 {
 	CRYPTO_cleanup_all_ex_data();
@@ -160,6 +351,14 @@
 	EVP_cleanup();
 }
 
+static void rsa_engine_remove(ENGINE *e)
+{
+	if (e) {
+		ENGINE_finish(e);
+		ENGINE_free(e);
+	}
+}
+
 static int rsa_sign_with_key(RSA *rsa, struct checksum_algo *checksum_algo,
 		const struct image_region region[], int region_count,
 		uint8_t **sigp, uint *sig_size)
@@ -235,13 +434,20 @@
 	     uint8_t **sigp, uint *sig_len)
 {
 	RSA *rsa;
+	ENGINE *e = NULL;
 	int ret;
 
 	ret = rsa_init();
 	if (ret)
 		return ret;
 
-	ret = rsa_get_priv_key(info->keydir, info->keyname, &rsa);
+	if (info->engine_id) {
+		ret = rsa_engine_init(info->engine_id, &e);
+		if (ret)
+			goto err_engine;
+	}
+
+	ret = rsa_get_priv_key(info->keydir, info->keyname, e, &rsa);
 	if (ret)
 		goto err_priv;
 	ret = rsa_sign_with_key(rsa, info->checksum, region,
@@ -250,6 +456,8 @@
 		goto err_sign;
 
 	RSA_free(rsa);
+	if (info->engine_id)
+		rsa_engine_remove(e);
 	rsa_remove();
 
 	return ret;
@@ -257,6 +465,9 @@
 err_sign:
 	RSA_free(rsa);
 err_priv:
+	if (info->engine_id)
+		rsa_engine_remove(e);
+err_engine:
 	rsa_remove();
 	return ret;
 }
@@ -446,14 +657,20 @@
 	int ret;
 	int bits;
 	RSA *rsa;
+	ENGINE *e = NULL;
 
 	debug("%s: Getting verification data\n", __func__);
-	ret = rsa_get_pub_key(info->keydir, info->keyname, &rsa);
+	if (info->engine_id) {
+		ret = rsa_engine_init(info->engine_id, &e);
+		if (ret)
+			return ret;
+	}
+	ret = rsa_get_pub_key(info->keydir, info->keyname, e, &rsa);
 	if (ret)
-		return ret;
+		goto err_get_pub_key;
 	ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared);
 	if (ret)
-		return ret;
+		goto err_get_params;
 	bits = BN_num_bits(modulus);
 	parent = fdt_subnode_offset(keydest, 0, FIT_SIG_NODENAME);
 	if (parent == -FDT_ERR_NOTFOUND) {
@@ -518,7 +735,12 @@
 	BN_free(modulus);
 	BN_free(r_squared);
 	if (ret)
-		return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
+		ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
+err_get_params:
+	RSA_free(rsa);
+err_get_pub_key:
+	if (info->engine_id)
+		rsa_engine_remove(e);
 
-	return 0;
+	return ret;
 }
diff --git a/tools/fit_image.c b/tools/fit_image.c
index efd8a97..4dc8bd8 100644
--- a/tools/fit_image.c
+++ b/tools/fit_image.c
@@ -59,7 +59,8 @@
 	if (!ret) {
 		ret = fit_add_verification_data(params->keydir, dest_blob, ptr,
 						params->comment,
-						params->require_keys);
+						params->require_keys,
+						params->engine_id);
 	}
 
 	if (dest_blob) {
diff --git a/tools/image-host.c b/tools/image-host.c
index c1a0122..5e4d690 100644
--- a/tools/image-host.c
+++ b/tools/image-host.c
@@ -149,7 +149,7 @@
 
 static int fit_image_setup_sig(struct image_sign_info *info,
 		const char *keydir, void *fit, const char *image_name,
-		int noffset, const char *require_keys)
+		int noffset, const char *require_keys, const char *engine_id)
 {
 	const char *node_name;
 	char *algo_name;
@@ -170,6 +170,7 @@
 	info->checksum = image_get_checksum_algo(algo_name);
 	info->crypto = image_get_crypto_algo(algo_name);
 	info->require_keys = require_keys;
+	info->engine_id = engine_id;
 	if (!info->checksum || !info->crypto) {
 		printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n",
 		       algo_name, node_name, image_name);
@@ -194,12 +195,13 @@
  * @size:	size of data in bytes
  * @comment:	Comment to add to signature nodes
  * @require_keys: Mark all keys as 'required'
+ * @engine_id:	Engine to use for signing
  * @return 0 if ok, -1 on error
  */
 static int fit_image_process_sig(const char *keydir, void *keydest,
 		void *fit, const char *image_name,
 		int noffset, const void *data, size_t size,
-		const char *comment, int require_keys)
+		const char *comment, int require_keys, const char *engine_id)
 {
 	struct image_sign_info info;
 	struct image_region region;
@@ -209,7 +211,7 @@
 	int ret;
 
 	if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset,
-				require_keys ? "image" : NULL))
+				require_keys ? "image" : NULL, engine_id))
 		return -1;
 
 	node_name = fit_get_name(fit, noffset, NULL);
@@ -288,11 +290,12 @@
  * @image_noffset: Requested component image node
  * @comment:	Comment to add to signature nodes
  * @require_keys: Mark all keys as 'required'
+ * @engine_id:	Engine to use for signing
  * @return: 0 on success, <0 on failure
  */
 int fit_image_add_verification_data(const char *keydir, void *keydest,
 		void *fit, int image_noffset, const char *comment,
-		int require_keys)
+		int require_keys, const char *engine_id)
 {
 	const char *image_name;
 	const void *data;
@@ -329,7 +332,7 @@
 				strlen(FIT_SIG_NODENAME))) {
 			ret = fit_image_process_sig(keydir, keydest,
 				fit, image_name, noffset, data, size,
-				comment, require_keys);
+				comment, require_keys, engine_id);
 		}
 		if (ret)
 			return ret;
@@ -569,7 +572,8 @@
 
 static int fit_config_process_sig(const char *keydir, void *keydest,
 		void *fit, const char *conf_name, int conf_noffset,
-		int noffset, const char *comment, int require_keys)
+		int noffset, const char *comment, int require_keys,
+		const char *engine_id)
 {
 	struct image_sign_info info;
 	const char *node_name;
@@ -587,7 +591,7 @@
 		return -1;
 
 	if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset,
-				require_keys ? "conf" : NULL))
+				require_keys ? "conf" : NULL, engine_id))
 		return -1;
 
 	ret = info.crypto->sign(&info, region, region_count, &value,
@@ -635,7 +639,7 @@
 
 static int fit_config_add_verification_data(const char *keydir, void *keydest,
 		void *fit, int conf_noffset, const char *comment,
-		int require_keys)
+		int require_keys, const char *engine_id)
 {
 	const char *conf_name;
 	int noffset;
@@ -654,7 +658,7 @@
 			     strlen(FIT_SIG_NODENAME))) {
 			ret = fit_config_process_sig(keydir, keydest,
 				fit, conf_name, conf_noffset, noffset, comment,
-				require_keys);
+				require_keys, engine_id);
 		}
 		if (ret)
 			return ret;
@@ -664,7 +668,8 @@
 }
 
 int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
-			      const char *comment, int require_keys)
+			      const char *comment, int require_keys,
+			      const char *engine_id)
 {
 	int images_noffset, confs_noffset;
 	int noffset;
@@ -687,7 +692,7 @@
 		 * i.e. component image node.
 		 */
 		ret = fit_image_add_verification_data(keydir, keydest,
-				fit, noffset, comment, require_keys);
+				fit, noffset, comment, require_keys, engine_id);
 		if (ret)
 			return ret;
 	}
@@ -710,7 +715,8 @@
 	     noffset = fdt_next_subnode(fit, noffset)) {
 		ret = fit_config_add_verification_data(keydir, keydest,
 						       fit, noffset, comment,
-						       require_keys);
+						       require_keys,
+						       engine_id);
 		if (ret)
 			return ret;
 	}
diff --git a/tools/imagetool.h b/tools/imagetool.h
index 15c2a0c..a8d5054 100644
--- a/tools/imagetool.h
+++ b/tools/imagetool.h
@@ -76,6 +76,7 @@
 	bool external_data;	/* Store data outside the FIT */
 	bool quiet;		/* Don't output text in normal operation */
 	unsigned int external_offset;	/* Add padding to external data */
+	const char *engine_id;	/* Engine to use for signing */
 };
 
 /*
diff --git a/tools/mkimage.c b/tools/mkimage.c
index f48135f..b0c98f6 100644
--- a/tools/mkimage.c
+++ b/tools/mkimage.c
@@ -98,14 +98,15 @@
 		"          -i => input filename for ramdisk file\n");
 #ifdef CONFIG_FIT_SIGNATURE
 	fprintf(stderr,
-		"Signing / verified boot options: [-E] [-k keydir] [-K dtb] [ -c <comment>] [-p addr] [-r]\n"
+		"Signing / verified boot options: [-E] [-k keydir] [-K dtb] [ -c <comment>] [-p addr] [-r] [-N engine]\n"
 		"          -E => place data outside of the FIT structure\n"
 		"          -k => set directory containing private keys\n"
 		"          -K => write public keys to this .dtb file\n"
 		"          -c => add comment in signature node\n"
 		"          -F => re-sign existing FIT image\n"
 		"          -p => place external data at a static position\n"
-		"          -r => mark keys used as 'required' in dtb\n");
+		"          -r => mark keys used as 'required' in dtb\n"
+		"          -N => engine to use for signing (pkcs11)\n");
 #else
 	fprintf(stderr,
 		"Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined)\n");
@@ -143,7 +144,7 @@
 	int opt;
 
 	while ((opt = getopt(argc, argv,
-			     "a:A:b:c:C:d:D:e:Ef:Fk:i:K:ln:p:O:rR:qsT:vVx")) != -1) {
+			     "a:A:b:c:C:d:D:e:Ef:Fk:i:K:ln:N:p:O:rR:qsT:vVx")) != -1) {
 		switch (opt) {
 		case 'a':
 			params.addr = strtoull(optarg, &ptr, 16);
@@ -224,6 +225,9 @@
 		case 'n':
 			params.imagename = optarg;
 			break;
+		case 'N':
+			params.engine_id = optarg;
+			break;
 		case 'O':
 			params.os = genimg_get_os_id(optarg);
 			if (params.os < 0) {