mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 07:04:28 +00:00
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>
This commit is contained in:
parent
b1c6a54a53
commit
f1ca1fdebf
7 changed files with 408 additions and 28 deletions
|
@ -385,6 +385,149 @@ Test Verified Boot Run: signed config with bad hash: OK
|
||||||
Test passed
|
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
|
Future Work
|
||||||
-----------
|
-----------
|
||||||
- Roll-back protection using a TPM is done using the tpm command. This can
|
- Roll-back protection using a TPM is done using the tpm command. This can
|
||||||
|
|
|
@ -965,6 +965,7 @@ int fit_set_timestamp(void *fit, int noffset, time_t timestamp);
|
||||||
* @fit: Pointer to the FIT format image header
|
* @fit: Pointer to the FIT format image header
|
||||||
* @comment: Comment to add to signature nodes
|
* @comment: Comment to add to signature nodes
|
||||||
* @require_keys: Mark all keys as 'required'
|
* @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.
|
* Adds hash values for all component images in the FIT blob.
|
||||||
* Hashes are calculated for all component images which have hash subnodes
|
* Hashes are calculated for all component images which have hash subnodes
|
||||||
|
@ -977,7 +978,8 @@ int fit_set_timestamp(void *fit, int noffset, time_t timestamp);
|
||||||
* libfdt error code, on failure
|
* libfdt error code, on failure
|
||||||
*/
|
*/
|
||||||
int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
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_image_verify(const void *fit, int noffset);
|
||||||
int fit_config_verify(const void *fit, int conf_noffset);
|
int fit_config_verify(const void *fit, int conf_noffset);
|
||||||
|
@ -1057,6 +1059,7 @@ struct image_sign_info {
|
||||||
const void *fdt_blob; /* FDT containing public keys */
|
const void *fdt_blob; /* FDT containing public keys */
|
||||||
int required_keynode; /* Node offset of key to use: -1=any */
|
int required_keynode; /* Node offset of key to use: -1=any */
|
||||||
const char *require_keys; /* Value for 'required' property */
|
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 */
|
#endif /* Allow struct image_region to always be defined for rsa.h */
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/engine.h>
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
||||||
#define HAVE_ERR_REMOVE_THREAD_STATE
|
#define HAVE_ERR_REMOVE_THREAD_STATE
|
||||||
|
@ -31,14 +32,14 @@ static int rsa_err(const char *msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @keydir: Directory containins the key
|
||||||
* @name Name of key file (will have a .crt extension)
|
* @name Name of key file (will have a .crt extension)
|
||||||
* @rsap Returns RSA object, or NULL on failure
|
* @rsap Returns RSA object, or NULL on failure
|
||||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
* @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];
|
char path[1024];
|
||||||
EVP_PKEY *key;
|
EVP_PKEY *key;
|
||||||
|
@ -96,14 +97,90 @@ err_cert:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
* @name Name of key file (will have a .key extension)
|
||||||
* @rsap Returns RSA object, or NULL on failure
|
* @rsap Returns RSA object, or NULL on failure
|
||||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
* @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];
|
char path[1024];
|
||||||
RSA *rsa;
|
RSA *rsa;
|
||||||
|
@ -130,6 +207,81 @@ static int rsa_get_priv_key(const char *keydir, const char *name, RSA **rsap)
|
||||||
return 0;
|
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)
|
static int rsa_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -148,6 +300,45 @@ static int rsa_init(void)
|
||||||
return 0;
|
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)
|
static void rsa_remove(void)
|
||||||
{
|
{
|
||||||
CRYPTO_cleanup_all_ex_data();
|
CRYPTO_cleanup_all_ex_data();
|
||||||
|
@ -160,6 +351,14 @@ static void rsa_remove(void)
|
||||||
EVP_cleanup();
|
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,
|
static int rsa_sign_with_key(RSA *rsa, struct checksum_algo *checksum_algo,
|
||||||
const struct image_region region[], int region_count,
|
const struct image_region region[], int region_count,
|
||||||
uint8_t **sigp, uint *sig_size)
|
uint8_t **sigp, uint *sig_size)
|
||||||
|
@ -235,13 +434,20 @@ int rsa_sign(struct image_sign_info *info,
|
||||||
uint8_t **sigp, uint *sig_len)
|
uint8_t **sigp, uint *sig_len)
|
||||||
{
|
{
|
||||||
RSA *rsa;
|
RSA *rsa;
|
||||||
|
ENGINE *e = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rsa_init();
|
ret = rsa_init();
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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)
|
if (ret)
|
||||||
goto err_priv;
|
goto err_priv;
|
||||||
ret = rsa_sign_with_key(rsa, info->checksum, region,
|
ret = rsa_sign_with_key(rsa, info->checksum, region,
|
||||||
|
@ -250,6 +456,8 @@ int rsa_sign(struct image_sign_info *info,
|
||||||
goto err_sign;
|
goto err_sign;
|
||||||
|
|
||||||
RSA_free(rsa);
|
RSA_free(rsa);
|
||||||
|
if (info->engine_id)
|
||||||
|
rsa_engine_remove(e);
|
||||||
rsa_remove();
|
rsa_remove();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -257,6 +465,9 @@ int rsa_sign(struct image_sign_info *info,
|
||||||
err_sign:
|
err_sign:
|
||||||
RSA_free(rsa);
|
RSA_free(rsa);
|
||||||
err_priv:
|
err_priv:
|
||||||
|
if (info->engine_id)
|
||||||
|
rsa_engine_remove(e);
|
||||||
|
err_engine:
|
||||||
rsa_remove();
|
rsa_remove();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -446,14 +657,20 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
|
||||||
int ret;
|
int ret;
|
||||||
int bits;
|
int bits;
|
||||||
RSA *rsa;
|
RSA *rsa;
|
||||||
|
ENGINE *e = NULL;
|
||||||
|
|
||||||
debug("%s: Getting verification data\n", __func__);
|
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)
|
if (ret)
|
||||||
return ret;
|
goto err_get_pub_key;
|
||||||
ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared);
|
ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err_get_params;
|
||||||
bits = BN_num_bits(modulus);
|
bits = BN_num_bits(modulus);
|
||||||
parent = fdt_subnode_offset(keydest, 0, FIT_SIG_NODENAME);
|
parent = fdt_subnode_offset(keydest, 0, FIT_SIG_NODENAME);
|
||||||
if (parent == -FDT_ERR_NOTFOUND) {
|
if (parent == -FDT_ERR_NOTFOUND) {
|
||||||
|
@ -518,7 +735,12 @@ done:
|
||||||
BN_free(modulus);
|
BN_free(modulus);
|
||||||
BN_free(r_squared);
|
BN_free(r_squared);
|
||||||
if (ret)
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,8 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc,
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = fit_add_verification_data(params->keydir, dest_blob, ptr,
|
ret = fit_add_verification_data(params->keydir, dest_blob, ptr,
|
||||||
params->comment,
|
params->comment,
|
||||||
params->require_keys);
|
params->require_keys,
|
||||||
|
params->engine_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dest_blob) {
|
if (dest_blob) {
|
||||||
|
|
|
@ -149,7 +149,7 @@ static int fit_image_write_sig(void *fit, int noffset, uint8_t *value,
|
||||||
|
|
||||||
static int fit_image_setup_sig(struct image_sign_info *info,
|
static int fit_image_setup_sig(struct image_sign_info *info,
|
||||||
const char *keydir, void *fit, const char *image_name,
|
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;
|
const char *node_name;
|
||||||
char *algo_name;
|
char *algo_name;
|
||||||
|
@ -170,6 +170,7 @@ static int fit_image_setup_sig(struct image_sign_info *info,
|
||||||
info->checksum = image_get_checksum_algo(algo_name);
|
info->checksum = image_get_checksum_algo(algo_name);
|
||||||
info->crypto = image_get_crypto_algo(algo_name);
|
info->crypto = image_get_crypto_algo(algo_name);
|
||||||
info->require_keys = require_keys;
|
info->require_keys = require_keys;
|
||||||
|
info->engine_id = engine_id;
|
||||||
if (!info->checksum || !info->crypto) {
|
if (!info->checksum || !info->crypto) {
|
||||||
printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n",
|
printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n",
|
||||||
algo_name, node_name, image_name);
|
algo_name, node_name, image_name);
|
||||||
|
@ -194,12 +195,13 @@ static int fit_image_setup_sig(struct image_sign_info *info,
|
||||||
* @size: size of data in bytes
|
* @size: size of data in bytes
|
||||||
* @comment: Comment to add to signature nodes
|
* @comment: Comment to add to signature nodes
|
||||||
* @require_keys: Mark all keys as 'required'
|
* @require_keys: Mark all keys as 'required'
|
||||||
|
* @engine_id: Engine to use for signing
|
||||||
* @return 0 if ok, -1 on error
|
* @return 0 if ok, -1 on error
|
||||||
*/
|
*/
|
||||||
static int fit_image_process_sig(const char *keydir, void *keydest,
|
static int fit_image_process_sig(const char *keydir, void *keydest,
|
||||||
void *fit, const char *image_name,
|
void *fit, const char *image_name,
|
||||||
int noffset, const void *data, size_t size,
|
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_sign_info info;
|
||||||
struct image_region region;
|
struct image_region region;
|
||||||
|
@ -209,7 +211,7 @@ static int fit_image_process_sig(const char *keydir, void *keydest,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset,
|
if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset,
|
||||||
require_keys ? "image" : NULL))
|
require_keys ? "image" : NULL, engine_id))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
node_name = fit_get_name(fit, noffset, NULL);
|
node_name = fit_get_name(fit, noffset, NULL);
|
||||||
|
@ -288,11 +290,12 @@ static int fit_image_process_sig(const char *keydir, void *keydest,
|
||||||
* @image_noffset: Requested component image node
|
* @image_noffset: Requested component image node
|
||||||
* @comment: Comment to add to signature nodes
|
* @comment: Comment to add to signature nodes
|
||||||
* @require_keys: Mark all keys as 'required'
|
* @require_keys: Mark all keys as 'required'
|
||||||
|
* @engine_id: Engine to use for signing
|
||||||
* @return: 0 on success, <0 on failure
|
* @return: 0 on success, <0 on failure
|
||||||
*/
|
*/
|
||||||
int fit_image_add_verification_data(const char *keydir, void *keydest,
|
int fit_image_add_verification_data(const char *keydir, void *keydest,
|
||||||
void *fit, int image_noffset, const char *comment,
|
void *fit, int image_noffset, const char *comment,
|
||||||
int require_keys)
|
int require_keys, const char *engine_id)
|
||||||
{
|
{
|
||||||
const char *image_name;
|
const char *image_name;
|
||||||
const void *data;
|
const void *data;
|
||||||
|
@ -329,7 +332,7 @@ int fit_image_add_verification_data(const char *keydir, void *keydest,
|
||||||
strlen(FIT_SIG_NODENAME))) {
|
strlen(FIT_SIG_NODENAME))) {
|
||||||
ret = fit_image_process_sig(keydir, keydest,
|
ret = fit_image_process_sig(keydir, keydest,
|
||||||
fit, image_name, noffset, data, size,
|
fit, image_name, noffset, data, size,
|
||||||
comment, require_keys);
|
comment, require_keys, engine_id);
|
||||||
}
|
}
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -569,7 +572,8 @@ static int fit_config_get_data(void *fit, int conf_noffset, int noffset,
|
||||||
|
|
||||||
static int fit_config_process_sig(const char *keydir, void *keydest,
|
static int fit_config_process_sig(const char *keydir, void *keydest,
|
||||||
void *fit, const char *conf_name, int conf_noffset,
|
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;
|
struct image_sign_info info;
|
||||||
const char *node_name;
|
const char *node_name;
|
||||||
|
@ -587,7 +591,7 @@ static int fit_config_process_sig(const char *keydir, void *keydest,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset,
|
if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset,
|
||||||
require_keys ? "conf" : NULL))
|
require_keys ? "conf" : NULL, engine_id))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = info.crypto->sign(&info, region, region_count, &value,
|
ret = info.crypto->sign(&info, region, region_count, &value,
|
||||||
|
@ -635,7 +639,7 @@ static int fit_config_process_sig(const char *keydir, void *keydest,
|
||||||
|
|
||||||
static int fit_config_add_verification_data(const char *keydir, void *keydest,
|
static int fit_config_add_verification_data(const char *keydir, void *keydest,
|
||||||
void *fit, int conf_noffset, const char *comment,
|
void *fit, int conf_noffset, const char *comment,
|
||||||
int require_keys)
|
int require_keys, const char *engine_id)
|
||||||
{
|
{
|
||||||
const char *conf_name;
|
const char *conf_name;
|
||||||
int noffset;
|
int noffset;
|
||||||
|
@ -654,7 +658,7 @@ static int fit_config_add_verification_data(const char *keydir, void *keydest,
|
||||||
strlen(FIT_SIG_NODENAME))) {
|
strlen(FIT_SIG_NODENAME))) {
|
||||||
ret = fit_config_process_sig(keydir, keydest,
|
ret = fit_config_process_sig(keydir, keydest,
|
||||||
fit, conf_name, conf_noffset, noffset, comment,
|
fit, conf_name, conf_noffset, noffset, comment,
|
||||||
require_keys);
|
require_keys, engine_id);
|
||||||
}
|
}
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -664,7 +668,8 @@ static int fit_config_add_verification_data(const char *keydir, void *keydest,
|
||||||
}
|
}
|
||||||
|
|
||||||
int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
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 images_noffset, confs_noffset;
|
||||||
int noffset;
|
int noffset;
|
||||||
|
@ -687,7 +692,7 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
||||||
* i.e. component image node.
|
* i.e. component image node.
|
||||||
*/
|
*/
|
||||||
ret = fit_image_add_verification_data(keydir, keydest,
|
ret = fit_image_add_verification_data(keydir, keydest,
|
||||||
fit, noffset, comment, require_keys);
|
fit, noffset, comment, require_keys, engine_id);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -710,7 +715,8 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
||||||
noffset = fdt_next_subnode(fit, noffset)) {
|
noffset = fdt_next_subnode(fit, noffset)) {
|
||||||
ret = fit_config_add_verification_data(keydir, keydest,
|
ret = fit_config_add_verification_data(keydir, keydest,
|
||||||
fit, noffset, comment,
|
fit, noffset, comment,
|
||||||
require_keys);
|
require_keys,
|
||||||
|
engine_id);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ struct image_tool_params {
|
||||||
bool external_data; /* Store data outside the FIT */
|
bool external_data; /* Store data outside the FIT */
|
||||||
bool quiet; /* Don't output text in normal operation */
|
bool quiet; /* Don't output text in normal operation */
|
||||||
unsigned int external_offset; /* Add padding to external data */
|
unsigned int external_offset; /* Add padding to external data */
|
||||||
|
const char *engine_id; /* Engine to use for signing */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -98,14 +98,15 @@ static void usage(const char *msg)
|
||||||
" -i => input filename for ramdisk file\n");
|
" -i => input filename for ramdisk file\n");
|
||||||
#ifdef CONFIG_FIT_SIGNATURE
|
#ifdef CONFIG_FIT_SIGNATURE
|
||||||
fprintf(stderr,
|
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"
|
" -E => place data outside of the FIT structure\n"
|
||||||
" -k => set directory containing private keys\n"
|
" -k => set directory containing private keys\n"
|
||||||
" -K => write public keys to this .dtb file\n"
|
" -K => write public keys to this .dtb file\n"
|
||||||
" -c => add comment in signature node\n"
|
" -c => add comment in signature node\n"
|
||||||
" -F => re-sign existing FIT image\n"
|
" -F => re-sign existing FIT image\n"
|
||||||
" -p => place external data at a static position\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
|
#else
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined)\n");
|
"Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined)\n");
|
||||||
|
@ -143,7 +144,7 @@ static void process_args(int argc, char **argv)
|
||||||
int opt;
|
int opt;
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv,
|
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) {
|
switch (opt) {
|
||||||
case 'a':
|
case 'a':
|
||||||
params.addr = strtoull(optarg, &ptr, 16);
|
params.addr = strtoull(optarg, &ptr, 16);
|
||||||
|
@ -224,6 +225,9 @@ static void process_args(int argc, char **argv)
|
||||||
case 'n':
|
case 'n':
|
||||||
params.imagename = optarg;
|
params.imagename = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
params.engine_id = optarg;
|
||||||
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
params.os = genimg_get_os_id(optarg);
|
params.os = genimg_get_os_id(optarg);
|
||||||
if (params.os < 0) {
|
if (params.os < 0) {
|
||||||
|
|
Loading…
Reference in a new issue