Merge branch '2020-10-12-assorted-encryption-changes'

- Fix verified boot on BE targets
- Add support for multiple required keys in verified boots
- Add support for Initialization Vectors in AES keys in FIT images
- Assorted fixes in the RSA code
This commit is contained in:
Tom Rini 2020-10-13 10:04:17 -04:00
commit 55fca74a5b
11 changed files with 190 additions and 36 deletions

View file

@ -94,9 +94,11 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info,
return -1;
}
info->iv = fdt_getprop(fit, cipher_noffset, "iv", NULL);
info->ivname = fdt_getprop(fit, cipher_noffset, "iv-name-hint", NULL);
if (!info->ivname) {
printf("Can't get IV name\n");
if (!info->iv && !info->ivname) {
printf("Can't get IV or IV name\n");
return -1;
}
@ -120,8 +122,12 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info,
* Search the cipher node in the u-boot fdt
* the path should be: /cipher/key-<algo>-<key>-<iv>
*/
snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s-%s",
FIT_CIPHER_NODENAME, algo_name, info->keyname, info->ivname);
if (info->ivname)
snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s-%s",
FIT_CIPHER_NODENAME, algo_name, info->keyname, info->ivname);
else
snprintf(node_path, sizeof(node_path), "/%s/key-%s-%s",
FIT_CIPHER_NODENAME, algo_name, info->keyname);
noffset = fdt_path_offset(fdt, node_path);
if (noffset < 0) {
@ -137,10 +143,12 @@ static int fit_image_setup_decrypt(struct image_cipher_info *info,
}
/* read iv */
info->iv = fdt_getprop(fdt, noffset, "iv", NULL);
if (!info->iv) {
printf("Can't get IV in cipher node '%s'\n", node_path);
return -1;
info->iv = fdt_getprop(fdt, noffset, "iv", NULL);
if (!info->iv) {
printf("Can't get IV in cipher node '%s'\n", node_path);
return -1;
}
}
return 0;

View file

@ -416,6 +416,10 @@ int fit_config_verify_required_sigs(const void *fit, int conf_noffset,
{
int noffset;
int sig_node;
int verified = 0;
int reqd_sigs = 0;
bool reqd_policy_all = true;
const char *reqd_mode;
/* Work out what we need to verify */
sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME);
@ -425,6 +429,14 @@ int fit_config_verify_required_sigs(const void *fit, int conf_noffset,
return 0;
}
/* Get required-mode policy property from DTB */
reqd_mode = fdt_getprop(sig_blob, sig_node, "required-mode", NULL);
if (reqd_mode && !strcmp(reqd_mode, "any"))
reqd_policy_all = false;
debug("%s: required-mode policy set to '%s'\n", __func__,
reqd_policy_all ? "all" : "any");
fdt_for_each_subnode(noffset, sig_blob, sig_node) {
const char *required;
int ret;
@ -433,15 +445,29 @@ int fit_config_verify_required_sigs(const void *fit, int conf_noffset,
NULL);
if (!required || strcmp(required, "conf"))
continue;
reqd_sigs++;
ret = fit_config_verify_sig(fit, conf_noffset, sig_blob,
noffset);
if (ret) {
printf("Failed to verify required signature '%s'\n",
fit_get_name(sig_blob, noffset, NULL));
return ret;
if (reqd_policy_all) {
printf("Failed to verify required signature '%s'\n",
fit_get_name(sig_blob, noffset, NULL));
return ret;
}
} else {
verified++;
if (!reqd_policy_all)
break;
}
}
if (reqd_sigs && !verified) {
printf("Failed to verify 'any' of the required signature(s)\n");
return -EPERM;
}
return 0;
}

View file

@ -386,6 +386,20 @@ that might be used by the target needs to be signed with 'required' keys.
This happens automatically as part of a bootm command when FITs are used.
For Signed Configurations, the default verification behavior can be changed by
the following optional property in /signature node in U-Boot's control FDT.
- required-mode: Valid values are "any" to allow verified boot to succeed if
the selected configuration is signed by any of the 'required' keys, and "all"
to allow verified boot to succeed if the selected configuration is signed by
all of the 'required' keys.
This property can be added to a binary device tree using fdtput as shown in
below examples::
fdtput -t s control.dtb /signature required-mode any
fdtput -t s control.dtb /signature required-mode all
Enabling FIT Verification
-------------------------

View file

@ -1463,7 +1463,7 @@ struct cipher_algo {
unsigned char **cipher, int *cipher_len);
int (*add_cipher_data)(struct image_cipher_info *info,
void *keydest);
void *keydest, void *fit, int node_noffset);
int (*decrypt)(struct image_cipher_info *info,
const void *cipher, size_t cipher_len,

View file

@ -13,7 +13,8 @@
int image_aes_encrypt(struct image_cipher_info *info,
const unsigned char *data, int size,
unsigned char **cipher, int *cipher_len);
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest);
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest,
void *fit, int node_noffset);
#else
int image_aes_encrypt(struct image_cipher_info *info,
const unsigned char *data, int size,
@ -22,7 +23,8 @@ int image_aes_encrypt(struct image_cipher_info *info,
return -ENXIO;
}
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest)
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest,
void *fit, int node_noffset)
{
return -ENXIO;
}

View file

@ -74,7 +74,8 @@ int image_aes_encrypt(struct image_cipher_info *info,
return ret;
}
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest)
int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest,
void *fit, int node_noffset)
{
int parent, node;
char name[128];
@ -97,8 +98,13 @@ int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest)
goto done;
/* Either create or overwrite the named key node */
snprintf(name, sizeof(name), "key-%s-%s-%s",
info->name, info->keyname, info->ivname);
if (info->ivname)
snprintf(name, sizeof(name), "key-%s-%s-%s",
info->name, info->keyname, info->ivname);
else
snprintf(name, sizeof(name), "key-%s-%s",
info->name, info->keyname);
node = fdt_subnode_offset(keydest, parent, name);
if (node == -FDT_ERR_NOTFOUND) {
node = fdt_add_subnode(keydest, parent, name);
@ -116,9 +122,17 @@ int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest)
ret = node;
}
if (!ret)
if (ret)
goto done;
if (info->ivname)
/* Store the IV in the u-boot device tree */
ret = fdt_setprop(keydest, node, "iv",
info->iv, info->cipher->iv_len);
else
/* Store the IV in the FIT image */
ret = fdt_setprop(fit, node_noffset, "iv",
info->iv, info->cipher->iv_len);
if (!ret)
ret = fdt_setprop(keydest, node, "key",

View file

@ -324,8 +324,7 @@ int hsearch_r(struct env_entry item, enum env_action action,
*/
unsigned hval2;
if (htab->table[idx].used == USED_DELETED
&& !first_deleted)
if (htab->table[idx].used == USED_DELETED)
first_deleted = idx;
ret = _compare_and_overwrite_entry(item, action, retval, htab,

View file

@ -25,6 +25,14 @@
#define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a)
#define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a))
static inline uint64_t fdt64_to_cpup(const void *p)
{
fdt64_t w;
memcpy(&w, p, sizeof(w));
return fdt64_to_cpu(w);
}
/* Default public exponent for backward compatibility */
#define RSA_DEFAULT_PUBEXP 65537
@ -263,8 +271,7 @@ int rsa_mod_exp_sw(const uint8_t *sig, uint32_t sig_len,
if (!prop->public_exponent)
key.exponent = RSA_DEFAULT_PUBEXP;
else
rsa_convert_big_endian((uint32_t *)&key.exponent,
prop->public_exponent, 2);
key.exponent = fdt64_to_cpup(prop->public_exponent);
if (!key.len || !prop->modulus || !prop->rr) {
debug("%s: Missing RSA key info", __func__);

View file

@ -439,12 +439,17 @@ static int rsa_verify_with_keynode(struct image_sign_info *info,
struct key_prop prop;
int length;
int ret = 0;
const char *algo;
if (node < 0) {
debug("%s: Skipping invalid node", __func__);
return -EBADF;
}
algo = fdt_getprop(blob, node, "algo", NULL);
if (strcmp(info->name, algo))
return -EFAULT;
prop.num_bits = fdtdec_get_int(blob, node, "rsa,num-bits", 0);
prop.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0);
@ -540,7 +545,7 @@ int rsa_verify(struct image_sign_info *info,
{
/* Reserve memory for maximum checksum-length */
uint8_t hash[info->crypto->key_len];
int ret = -EACCES;
int ret;
/*
* Verify that the checksum-length does not exceed the

View file

@ -126,6 +126,23 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
cons.log.action('%s: Sign images' % sha_algo)
util.run_and_log(cons, args)
def sign_fit_norequire(sha_algo, options):
"""Sign the FIT
Signs the FIT and writes the signature into it. It also writes the
public key into the dtb. It does not mark key as 'required' in dtb.
Args:
sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
use.
options: Options to provide to mkimage.
"""
args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, fit]
if options:
args += options.split(' ')
cons.log.action('%s: Sign images' % sha_algo)
util.run_and_log(cons, args)
def replace_fit_totalsize(size):
"""Replace FIT header's totalsize with something greater.
@ -279,15 +296,40 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
# Build the FIT with dev key (keys NOT required). This adds the
# signature into sandbox-u-boot.dtb, NOT marked 'required'.
make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
sign_fit(sha_algo, sign_options)
sign_fit_norequire(sha_algo, sign_options)
# So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
# Only the prod key is set as 'required'. But FIT we just built has
# a dev signature only (sign_fit() overwrites the FIT).
# a dev signature only (sign_fit_norequire() overwrites the FIT).
# Try to boot the FIT with dev key. This FIT should not be accepted by
# U-Boot because the prod key is required.
run_bootm(sha_algo, 'required key', '', False)
# Build the FIT with dev key (keys required) and sign it. This puts the
# signature into sandbox-u-boot.dtb, marked 'required'.
make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
sign_fit(sha_algo, sign_options)
# Set the required-mode policy to "any".
# So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
# Both the dev and prod key are set as 'required'. But FIT we just built has
# a dev signature only (sign_fit() overwrites the FIT).
# Try to boot the FIT with dev key. This FIT should be accepted by
# U-Boot because the dev key is required and policy is "any" required key.
util.run_and_log(cons, 'fdtput -t s %s /signature required-mode any' %
(dtb))
run_bootm(sha_algo, 'multi required key', 'dev+', True)
# Set the required-mode policy to "all".
# So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
# Both the dev and prod key are set as 'required'. But FIT we just built has
# a dev signature only (sign_fit() overwrites the FIT).
# Try to boot the FIT with dev key. This FIT should not be accepted by
# U-Boot because the prod key is required and policy is "all" required key
util.run_and_log(cons, 'fdtput -t s %s /signature required-mode all' %
(dtb))
run_bootm(sha_algo, 'multi required key', '', False)
cons = u_boot_console
tmpdir = cons.config.result_dir + '/'
datadir = cons.config.source_dir + '/test/py/tests/vboot/'

View file

@ -320,6 +320,36 @@ err:
return ret;
}
static int get_random_data(void *data, int size)
{
unsigned char *tmp = data;
struct timespec date;
int i, ret = 0;
if (!tmp) {
printf("%s: pointer data is NULL\n", __func__);
ret = -1;
goto out;
}
ret = clock_gettime(CLOCK_MONOTONIC, &date);
if (ret < 0) {
printf("%s: clock_gettime has failed (err=%d, str=%s)\n",
__func__, ret, strerror(ret));
goto out;
}
srand(date.tv_nsec);
for (i = 0; i < size; i++) {
*tmp = rand() & 0xff;
tmp++;
}
out:
return ret;
}
static int fit_image_setup_cipher(struct image_cipher_info *info,
const char *keydir, void *fit,
const char *image_name, int image_noffset,
@ -345,13 +375,13 @@ static int fit_image_setup_cipher(struct image_cipher_info *info,
goto out;
}
/* Read the IV name */
/*
* Read the IV name
*
* If this property is not provided then mkimage will generate
* a random IV and store it in the FIT image
*/
info->ivname = fdt_getprop(fit, noffset, "iv-name-hint", NULL);
if (!info->ivname) {
printf("Can't get iv name for cipher in image '%s'\n",
image_name);
goto out;
}
info->fit = fit;
info->node_noffset = noffset;
@ -377,17 +407,23 @@ static int fit_image_setup_cipher(struct image_cipher_info *info,
if (ret < 0)
goto out;
/* Read the IV in the file */
snprintf(filename, sizeof(filename), "%s/%s%s",
info->keydir, info->ivname, ".bin");
info->iv = malloc(info->cipher->iv_len);
if (!info->iv) {
printf("Can't allocate memory for iv\n");
ret = -1;
goto out;
}
ret = fit_image_read_data(filename, (unsigned char *)info->iv,
info->cipher->iv_len);
if (info->ivname) {
/* Read the IV in the file */
snprintf(filename, sizeof(filename), "%s/%s%s",
info->keydir, info->ivname, ".bin");
ret = fit_image_read_data(filename, (unsigned char *)info->iv,
info->cipher->iv_len);
} else {
/* Generate an ramdom IV */
ret = get_random_data((void *)info->iv, info->cipher->iv_len);
}
out:
return ret;
@ -453,9 +489,10 @@ fit_image_process_cipher(const char *keydir, void *keydest, void *fit,
* Write the public key into the supplied FDT file; this might fail
* several times, since we try signing with successively increasing
* size values
* And, if needed, write the iv in the FIT file
*/
if (keydest) {
ret = info.cipher->add_cipher_data(&info, keydest);
ret = info.cipher->add_cipher_data(&info, keydest, fit, node_noffset);
if (ret) {
printf("Failed to add verification data for cipher '%s' in image '%s'\n",
info.keyname, image_name);