blob: 5e01b853c502cf0943a2432f64f3f0f72359a044 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass10a1eca2013-05-07 06:11:54 +00002/*
3 * Copyright (c) 2013, Google Inc.
4 *
5 * (C) Copyright 2008 Semihalf
6 *
7 * (C) Copyright 2000-2006
8 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
Simon Glass10a1eca2013-05-07 06:11:54 +00009 */
10
11#include "mkimage.h"
Simon Glassa51991d2014-06-12 07:24:53 -060012#include <bootm.h>
Masahiro Yamada6dd10522020-04-16 18:30:18 +090013#include <fdt_region.h>
Simon Glass10a1eca2013-05-07 06:11:54 +000014#include <image.h>
Simon Glassfbabc0f2013-06-13 15:10:01 -070015#include <version.h>
Simon Glass10a1eca2013-05-07 06:11:54 +000016
Paul-Erwan Riodcfb6332023-12-21 08:26:11 +010017#if CONFIG_IS_ENABLED(FIT_SIGNATURE)
Philippe Reynes3e3899c2022-03-28 22:57:02 +020018#include <openssl/pem.h>
19#include <openssl/evp.h>
Paul-Erwan Riodcfb6332023-12-21 08:26:11 +010020#endif
Philippe Reynes3e3899c2022-03-28 22:57:02 +020021
Simon Glass10a1eca2013-05-07 06:11:54 +000022/**
Simon Glass0ce6e3d2013-05-07 06:11:56 +000023 * fit_set_hash_value - set hash value in requested has node
24 * @fit: pointer to the FIT format image header
25 * @noffset: hash node offset
26 * @value: hash value to be set
27 * @value_len: hash value length
28 *
29 * fit_set_hash_value() attempts to set hash value in a node at offset
30 * given and returns operation status to the caller.
31 *
32 * returns
33 * 0, on success
34 * -1, on failure
35 */
36static int fit_set_hash_value(void *fit, int noffset, uint8_t *value,
37 int value_len)
38{
39 int ret;
40
41 ret = fdt_setprop(fit, noffset, FIT_VALUE_PROP, value, value_len);
42 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +030043 fprintf(stderr, "Can't set hash '%s' property for '%s' node(%s)\n",
44 FIT_VALUE_PROP, fit_get_name(fit, noffset, NULL),
45 fdt_strerror(ret));
Simon Glassec539f72016-07-03 09:40:44 -060046 return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
Simon Glass0ce6e3d2013-05-07 06:11:56 +000047 }
48
49 return 0;
50}
51
52/**
Simon Glass25d6e6b2013-05-07 06:11:55 +000053 * fit_image_process_hash - Process a single subnode of the images/ node
54 *
55 * Check each subnode and process accordingly. For hash nodes we generate
Simon Glass6a0efc82021-11-12 12:28:06 -070056 * a hash of the supplied data and store it in the node.
Simon Glass25d6e6b2013-05-07 06:11:55 +000057 *
58 * @fit: pointer to the FIT format image header
Simon Glass6a0efc82021-11-12 12:28:06 -070059 * @image_name: name of image being processed (used to display errors)
Simon Glass25d6e6b2013-05-07 06:11:55 +000060 * @noffset: subnode offset
61 * @data: data to process
62 * @size: size of data in bytes
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +010063 * Return: 0 if ok, -1 on error
Simon Glass25d6e6b2013-05-07 06:11:55 +000064 */
65static int fit_image_process_hash(void *fit, const char *image_name,
66 int noffset, const void *data, size_t size)
67{
68 uint8_t value[FIT_MAX_HASH_LEN];
Simon Glassee382652013-05-07 06:12:01 +000069 const char *node_name;
Simon Glass25d6e6b2013-05-07 06:11:55 +000070 int value_len;
Jan Kiszka27beec22022-01-14 10:21:17 +010071 const char *algo;
Simon Glassec539f72016-07-03 09:40:44 -060072 int ret;
Simon Glass25d6e6b2013-05-07 06:11:55 +000073
Simon Glassee382652013-05-07 06:12:01 +000074 node_name = fit_get_name(fit, noffset, NULL);
Simon Glass25d6e6b2013-05-07 06:11:55 +000075
76 if (fit_image_hash_get_algo(fit, noffset, &algo)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +030077 fprintf(stderr,
78 "Can't get hash algo property for '%s' hash node in '%s' image node\n",
79 node_name, image_name);
Simon Glassec539f72016-07-03 09:40:44 -060080 return -ENOENT;
Simon Glass25d6e6b2013-05-07 06:11:55 +000081 }
82
83 if (calculate_hash(data, size, algo, value, &value_len)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +030084 fprintf(stderr,
85 "Unsupported hash algorithm (%s) for '%s' hash node in '%s' image node\n",
86 algo, node_name, image_name);
Simon Glassec539f72016-07-03 09:40:44 -060087 return -EPROTONOSUPPORT;
Simon Glass25d6e6b2013-05-07 06:11:55 +000088 }
89
Simon Glassec539f72016-07-03 09:40:44 -060090 ret = fit_set_hash_value(fit, noffset, value, value_len);
91 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +030092 fprintf(stderr, "Can't set hash value for '%s' hash node in '%s' image node\n",
93 node_name, image_name);
Simon Glassec539f72016-07-03 09:40:44 -060094 return ret;
Simon Glass25d6e6b2013-05-07 06:11:55 +000095 }
96
97 return 0;
98}
99
100/**
Simon Glassfbabc0f2013-06-13 15:10:01 -0700101 * fit_image_write_sig() - write the signature to a FIT
Simon Glass10a1eca2013-05-07 06:11:54 +0000102 *
Simon Glassfbabc0f2013-06-13 15:10:01 -0700103 * This writes the signature and signer data to the FIT.
104 *
105 * @fit: pointer to the FIT format image header
106 * @noffset: hash node offset
107 * @value: signature value to be set
108 * @value_len: signature value length
109 * @comment: Text comment to write (NULL for none)
110 *
111 * returns
112 * 0, on success
113 * -FDT_ERR_..., on failure
114 */
115static int fit_image_write_sig(void *fit, int noffset, uint8_t *value,
116 int value_len, const char *comment, const char *region_prop,
Jan Kiszka4043f322022-01-14 10:21:19 +0100117 int region_proplen, const char *cmdname, const char *algo_name)
Simon Glassfbabc0f2013-06-13 15:10:01 -0700118{
119 int string_size;
120 int ret;
121
122 /*
123 * Get the current string size, before we update the FIT and add
124 * more
125 */
126 string_size = fdt_size_dt_strings(fit);
127
128 ret = fdt_setprop(fit, noffset, FIT_VALUE_PROP, value, value_len);
129 if (!ret) {
130 ret = fdt_setprop_string(fit, noffset, "signer-name",
131 "mkimage");
132 }
133 if (!ret) {
134 ret = fdt_setprop_string(fit, noffset, "signer-version",
135 PLAIN_VERSION);
136 }
137 if (comment && !ret)
138 ret = fdt_setprop_string(fit, noffset, "comment", comment);
Alex Kiernan697fcdc2018-06-20 20:10:52 +0000139 if (!ret) {
140 time_t timestamp = imagetool_get_source_date(cmdname,
141 time(NULL));
Ming Liuf75f1d72021-05-31 09:04:51 +0200142 uint32_t t = cpu_to_uimage(timestamp);
Alex Kiernan697fcdc2018-06-20 20:10:52 +0000143
Ming Liuf75f1d72021-05-31 09:04:51 +0200144 ret = fdt_setprop(fit, noffset, FIT_TIMESTAMP_PROP, &t,
145 sizeof(uint32_t));
Alex Kiernan697fcdc2018-06-20 20:10:52 +0000146 }
Simon Glassfbabc0f2013-06-13 15:10:01 -0700147 if (region_prop && !ret) {
148 uint32_t strdata[2];
149
150 ret = fdt_setprop(fit, noffset, "hashed-nodes",
151 region_prop, region_proplen);
Teddy Reed9767bf42018-06-09 11:45:20 -0400152 /* This is a legacy offset, it is unused, and must remain 0. */
Simon Glassfbabc0f2013-06-13 15:10:01 -0700153 strdata[0] = 0;
154 strdata[1] = cpu_to_fdt32(string_size);
155 if (!ret) {
156 ret = fdt_setprop(fit, noffset, "hashed-strings",
157 strdata, sizeof(strdata));
158 }
159 }
Jan Kiszka4043f322022-01-14 10:21:19 +0100160 if (algo_name && !ret)
161 ret = fdt_setprop_string(fit, noffset, "algo", algo_name);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700162
163 return ret;
164}
165
166static int fit_image_setup_sig(struct image_sign_info *info,
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600167 const char *keydir, const char *keyfile, void *fit,
168 const char *image_name, int noffset, const char *require_keys,
Jan Kiszka4043f322022-01-14 10:21:19 +0100169 const char *engine_id, const char *algo_name)
Simon Glassfbabc0f2013-06-13 15:10:01 -0700170{
171 const char *node_name;
Philippe Reynes12468352018-11-14 13:51:00 +0100172 const char *padding_name;
Simon Glassfbabc0f2013-06-13 15:10:01 -0700173
174 node_name = fit_get_name(fit, noffset, NULL);
Jan Kiszka4043f322022-01-14 10:21:19 +0100175 if (!algo_name) {
176 if (fit_image_hash_get_algo(fit, noffset, &algo_name)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300177 fprintf(stderr,
178 "Can't get algo property for '%s' signature node in '%s' image node\n",
179 node_name, image_name);
Jan Kiszka4043f322022-01-14 10:21:19 +0100180 return -1;
181 }
Simon Glassfbabc0f2013-06-13 15:10:01 -0700182 }
183
Philippe Reynes12468352018-11-14 13:51:00 +0100184 padding_name = fdt_getprop(fit, noffset, "padding", NULL);
185
Simon Glassfbabc0f2013-06-13 15:10:01 -0700186 memset(info, '\0', sizeof(*info));
187 info->keydir = keydir;
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600188 info->keyfile = keyfile;
Simon Glassd7aabcc2020-03-18 11:44:06 -0600189 info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700190 info->fit = fit;
191 info->node_offset = noffset;
Masahiro Yamadac1f137b2017-10-27 13:25:21 +0900192 info->name = strdup(algo_name);
Andrew Duda6616c822016-11-08 18:53:41 +0000193 info->checksum = image_get_checksum_algo(algo_name);
194 info->crypto = image_get_crypto_algo(algo_name);
Philippe Reynes12468352018-11-14 13:51:00 +0100195 info->padding = image_get_padding_algo(padding_name);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700196 info->require_keys = require_keys;
George McCollister23d14892017-01-06 13:14:17 -0600197 info->engine_id = engine_id;
Andrew Duda6616c822016-11-08 18:53:41 +0000198 if (!info->checksum || !info->crypto) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300199 fprintf(stderr,
200 "Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n",
201 algo_name, node_name, image_name);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700202 return -1;
203 }
204
205 return 0;
206}
207
208/**
209 * fit_image_process_sig- Process a single subnode of the images/ node
210 *
211 * Check each subnode and process accordingly. For signature nodes we
Simon Glass6a0efc82021-11-12 12:28:06 -0700212 * generate a signed hash of the supplied data and store it in the node.
Simon Glassfbabc0f2013-06-13 15:10:01 -0700213 *
214 * @keydir: Directory containing keys to use for signing
Simon Glass6a0efc82021-11-12 12:28:06 -0700215 * @keydest: Destination FDT blob to write public keys into (NULL if none)
Simon Glassfbabc0f2013-06-13 15:10:01 -0700216 * @fit: pointer to the FIT format image header
Simon Glass6a0efc82021-11-12 12:28:06 -0700217 * @image_name: name of image being processed (used to display errors)
Simon Glassfbabc0f2013-06-13 15:10:01 -0700218 * @noffset: subnode offset
219 * @data: data to process
220 * @size: size of data in bytes
221 * @comment: Comment to add to signature nodes
222 * @require_keys: Mark all keys as 'required'
George McCollister23d14892017-01-06 13:14:17 -0600223 * @engine_id: Engine to use for signing
Simon Glass89c3fb62021-11-12 12:28:12 -0700224 * Return: keydest node if @keydest is non-NULL, else 0 if none; -ve error code
225 * on failure
Simon Glassfbabc0f2013-06-13 15:10:01 -0700226 */
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600227static int fit_image_process_sig(const char *keydir, const char *keyfile,
228 void *keydest, void *fit, const char *image_name,
Simon Glassfbabc0f2013-06-13 15:10:01 -0700229 int noffset, const void *data, size_t size,
Alex Kiernan697fcdc2018-06-20 20:10:52 +0000230 const char *comment, int require_keys, const char *engine_id,
Jan Kiszka4043f322022-01-14 10:21:19 +0100231 const char *cmdname, const char *algo_name)
Simon Glassfbabc0f2013-06-13 15:10:01 -0700232{
233 struct image_sign_info info;
234 struct image_region region;
235 const char *node_name;
236 uint8_t *value;
237 uint value_len;
238 int ret;
239
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600240 if (fit_image_setup_sig(&info, keydir, keyfile, fit, image_name,
241 noffset, require_keys ? "image" : NULL,
Jan Kiszka4043f322022-01-14 10:21:19 +0100242 engine_id, algo_name))
Simon Glassfbabc0f2013-06-13 15:10:01 -0700243 return -1;
244
245 node_name = fit_get_name(fit, noffset, NULL);
246 region.data = data;
247 region.size = size;
Andrew Duda6616c822016-11-08 18:53:41 +0000248 ret = info.crypto->sign(&info, &region, 1, &value, &value_len);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700249 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300250 fprintf(stderr, "Failed to sign '%s' signature node in '%s' image node: %d\n",
251 node_name, image_name, ret);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700252
253 /* We allow keys to be missing */
254 if (ret == -ENOENT)
255 return 0;
256 return -1;
257 }
258
259 ret = fit_image_write_sig(fit, noffset, value, value_len, comment,
Jan Kiszka4043f322022-01-14 10:21:19 +0100260 NULL, 0, cmdname, algo_name);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700261 if (ret) {
Simon Glass802aa822014-06-02 22:04:53 -0600262 if (ret == -FDT_ERR_NOSPACE)
263 return -ENOSPC;
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300264 fprintf(stderr,
265 "Can't write signature for '%s' signature node in '%s' conf node: %s\n",
266 node_name, image_name, fdt_strerror(ret));
Simon Glassfbabc0f2013-06-13 15:10:01 -0700267 return -1;
268 }
269 free(value);
270
271 /* Get keyname again, as FDT has changed and invalidated our pointer */
Simon Glassd7aabcc2020-03-18 11:44:06 -0600272 info.keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL);
Simon Glassfbabc0f2013-06-13 15:10:01 -0700273
mario.six@gdsys.ccdc4053a2016-07-22 08:58:40 +0200274 /*
275 * Write the public key into the supplied FDT file; this might fail
mario.six@gdsys.ccd019e152016-07-19 11:07:06 +0200276 * several times, since we try signing with successively increasing
mario.six@gdsys.ccdc4053a2016-07-22 08:58:40 +0200277 * size values
278 */
Masahiro Yamada01486762017-10-27 15:04:20 +0900279 if (keydest) {
280 ret = info.crypto->add_verify_data(&info, keydest);
Simon Glass94336dc2021-11-12 12:28:11 -0700281 if (ret < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300282 fprintf(stderr,
283 "Failed to add verification data for '%s' signature node in '%s' image node\n",
284 node_name, image_name);
Masahiro Yamada01486762017-10-27 15:04:20 +0900285 return ret;
286 }
Simon Glass89c3fb62021-11-12 12:28:12 -0700287 /* Return the node that was written to */
288 return ret;
Masahiro Yamada01486762017-10-27 15:04:20 +0900289 }
Simon Glassfbabc0f2013-06-13 15:10:01 -0700290
291 return 0;
292}
293
Philippe Reynes3148e422019-12-18 18:25:41 +0100294static int fit_image_read_data(char *filename, unsigned char *data,
295 int expected_size)
296{
297 struct stat sbuf;
298 int fd, ret = -1;
299 ssize_t n;
300
301 /* Open file */
302 fd = open(filename, O_RDONLY | O_BINARY);
303 if (fd < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300304 fprintf(stderr, "Can't open file %s (err=%d => %s)\n",
305 filename, errno, strerror(errno));
Philippe Reynes3148e422019-12-18 18:25:41 +0100306 return -1;
307 }
308
309 /* Compute file size */
310 if (fstat(fd, &sbuf) < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300311 fprintf(stderr, "Can't fstat file %s (err=%d => %s)\n",
312 filename, errno, strerror(errno));
Philippe Reynes3148e422019-12-18 18:25:41 +0100313 goto err;
314 }
315
316 /* Check file size */
317 if (sbuf.st_size != expected_size) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300318 fprintf(stderr, "File %s don't have the expected size (size=%lld, expected=%d)\n",
319 filename, (long long)sbuf.st_size, expected_size);
Philippe Reynes3148e422019-12-18 18:25:41 +0100320 goto err;
321 }
322
323 /* Read data */
324 n = read(fd, data, sbuf.st_size);
325 if (n < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300326 fprintf(stderr, "Can't read file %s (err=%d => %s)\n",
327 filename, errno, strerror(errno));
Philippe Reynes3148e422019-12-18 18:25:41 +0100328 goto err;
329 }
330
331 /* Check that we have read all the file */
332 if (n != sbuf.st_size) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300333 fprintf(stderr, "Can't read all file %s (read %zd bytes, expected %lld)\n",
334 filename, n, (long long)sbuf.st_size);
Philippe Reynes3148e422019-12-18 18:25:41 +0100335 goto err;
336 }
337
338 ret = 0;
339
340err:
341 close(fd);
342 return ret;
343}
344
Hugo Cornelis92814842024-01-08 15:24:30 +0100345static int fit_image_read_key_iv_data(const char *keydir, const char *key_iv_name,
346 unsigned char *key_iv_data, int expected_size)
347{
348 char filename[PATH_MAX];
Hugo Cornelis23b571e2024-03-21 12:22:22 +0100349 int ret;
Hugo Cornelis92814842024-01-08 15:24:30 +0100350
351 ret = snprintf(filename, sizeof(filename), "%s/%s%s",
352 keydir, key_iv_name, ".bin");
353 if (ret >= sizeof(filename)) {
Hugo Cornelis23b571e2024-03-21 12:22:22 +0100354 fprintf(stderr, "Can't format the key or IV filename when setting up the cipher: insufficient buffer space\n");
355 return -1;
Hugo Cornelis92814842024-01-08 15:24:30 +0100356 }
357 if (ret < 0) {
Hugo Cornelis23b571e2024-03-21 12:22:22 +0100358 fprintf(stderr, "Can't format the key or IV filename when setting up the cipher: snprintf error\n");
359 return -1;
Hugo Cornelis92814842024-01-08 15:24:30 +0100360 }
361
362 ret = fit_image_read_data(filename, key_iv_data, expected_size);
363
364 return ret;
365}
366
Philippe Reynes64d67c42020-09-17 15:01:46 +0200367static int get_random_data(void *data, int size)
368{
369 unsigned char *tmp = data;
370 struct timespec date;
Simon Glass7a1745c2021-05-13 19:39:20 -0600371 int i, ret;
Philippe Reynes64d67c42020-09-17 15:01:46 +0200372
373 if (!tmp) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300374 fprintf(stderr, "%s: pointer data is NULL\n", __func__);
Philippe Reynes64d67c42020-09-17 15:01:46 +0200375 ret = -1;
376 goto out;
377 }
378
379 ret = clock_gettime(CLOCK_MONOTONIC, &date);
Simon Glass7a1745c2021-05-13 19:39:20 -0600380 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300381 fprintf(stderr, "%s: clock_gettime has failed (%s)\n", __func__,
382 strerror(errno));
Philippe Reynes64d67c42020-09-17 15:01:46 +0200383 goto out;
384 }
385
Philippe Reynes95f2ab22020-11-13 16:37:46 +0100386 srandom(date.tv_nsec);
Philippe Reynes64d67c42020-09-17 15:01:46 +0200387
388 for (i = 0; i < size; i++) {
Philippe Reynes95f2ab22020-11-13 16:37:46 +0100389 *tmp = random() & 0xff;
Philippe Reynes64d67c42020-09-17 15:01:46 +0200390 tmp++;
391 }
392
393 out:
394 return ret;
395}
396
Philippe Reynes3148e422019-12-18 18:25:41 +0100397static int fit_image_setup_cipher(struct image_cipher_info *info,
398 const char *keydir, void *fit,
399 const char *image_name, int image_noffset,
Patrick Oppenlanderd586fc32020-07-30 14:22:13 +1000400 int noffset)
Philippe Reynes3148e422019-12-18 18:25:41 +0100401{
402 char *algo_name;
Philippe Reynes3148e422019-12-18 18:25:41 +0100403 int ret = -1;
404
405 if (fit_image_cipher_get_algo(fit, noffset, &algo_name)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300406 fprintf(stderr, "Can't get algo name for cipher in image '%s'\n",
407 image_name);
Philippe Reynes3148e422019-12-18 18:25:41 +0100408 goto out;
409 }
410
411 info->keydir = keydir;
412
413 /* Read the key name */
Simon Glassd7aabcc2020-03-18 11:44:06 -0600414 info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL);
Philippe Reynes3148e422019-12-18 18:25:41 +0100415 if (!info->keyname) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300416 fprintf(stderr, "Can't get key name for cipher in image '%s'\n",
417 image_name);
Philippe Reynes3148e422019-12-18 18:25:41 +0100418 goto out;
419 }
420
Philippe Reynes64d67c42020-09-17 15:01:46 +0200421 /*
422 * Read the IV name
423 *
424 * If this property is not provided then mkimage will generate
425 * a random IV and store it in the FIT image
426 */
Philippe Reynes3148e422019-12-18 18:25:41 +0100427 info->ivname = fdt_getprop(fit, noffset, "iv-name-hint", NULL);
Philippe Reynes3148e422019-12-18 18:25:41 +0100428
429 info->fit = fit;
430 info->node_noffset = noffset;
431 info->name = algo_name;
432
433 info->cipher = image_get_cipher_algo(algo_name);
434 if (!info->cipher) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300435 fprintf(stderr, "Can't get algo for cipher '%s'\n", image_name);
Philippe Reynes3148e422019-12-18 18:25:41 +0100436 goto out;
437 }
438
Philippe Reynes3148e422019-12-18 18:25:41 +0100439 info->key = malloc(info->cipher->key_len);
440 if (!info->key) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300441 fprintf(stderr, "Can't allocate memory for key\n");
Philippe Reynes3148e422019-12-18 18:25:41 +0100442 ret = -1;
443 goto out;
444 }
Hugo Cornelis92814842024-01-08 15:24:30 +0100445
446 /* Read the key in the file */
447 ret = fit_image_read_key_iv_data(info->keydir, info->keyname,
448 (unsigned char *)info->key,
449 info->cipher->key_len);
Philippe Reynes3148e422019-12-18 18:25:41 +0100450 if (ret < 0)
451 goto out;
452
Philippe Reynes3148e422019-12-18 18:25:41 +0100453 info->iv = malloc(info->cipher->iv_len);
454 if (!info->iv) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300455 fprintf(stderr, "Can't allocate memory for iv\n");
Philippe Reynes3148e422019-12-18 18:25:41 +0100456 ret = -1;
457 goto out;
458 }
Philippe Reynes64d67c42020-09-17 15:01:46 +0200459
460 if (info->ivname) {
461 /* Read the IV in the file */
Hugo Cornelis92814842024-01-08 15:24:30 +0100462 ret = fit_image_read_key_iv_data(info->keydir, info->ivname,
463 (unsigned char *)info->iv,
464 info->cipher->iv_len);
465 if (ret < 0)
466 goto out;
Philippe Reynes64d67c42020-09-17 15:01:46 +0200467 } else {
468 /* Generate an ramdom IV */
469 ret = get_random_data((void *)info->iv, info->cipher->iv_len);
470 }
Philippe Reynes3148e422019-12-18 18:25:41 +0100471
472 out:
473 return ret;
474}
475
476int fit_image_write_cipher(void *fit, int image_noffset, int noffset,
477 const void *data, size_t size,
478 unsigned char *data_ciphered, int data_ciphered_len)
479{
480 int ret = -1;
481
Patrick Oppenlanderbe472112020-07-30 14:22:14 +1000482 /* Replace data with ciphered data */
Philippe Reynes3148e422019-12-18 18:25:41 +0100483 ret = fdt_setprop(fit, image_noffset, FIT_DATA_PROP,
484 data_ciphered, data_ciphered_len);
Patrick Oppenlanderbe472112020-07-30 14:22:14 +1000485 if (ret == -FDT_ERR_NOSPACE) {
486 ret = -ENOSPC;
487 goto out;
488 }
Philippe Reynes3148e422019-12-18 18:25:41 +0100489 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300490 fprintf(stderr, "Can't replace data with ciphered data (err = %d)\n", ret);
Philippe Reynes3148e422019-12-18 18:25:41 +0100491 goto out;
492 }
493
494 /* add non ciphered data size */
495 ret = fdt_setprop_u32(fit, image_noffset, "data-size-unciphered", size);
Patrick Oppenlanderbe472112020-07-30 14:22:14 +1000496 if (ret == -FDT_ERR_NOSPACE) {
497 ret = -ENOSPC;
498 goto out;
499 }
Philippe Reynes3148e422019-12-18 18:25:41 +0100500 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300501 fprintf(stderr, "Can't add unciphered data size (err = %d)\n", ret);
Philippe Reynes3148e422019-12-18 18:25:41 +0100502 goto out;
503 }
504
505 out:
506 return ret;
507}
508
509static int
510fit_image_process_cipher(const char *keydir, void *keydest, void *fit,
511 const char *image_name, int image_noffset,
Patrick Oppenlanderd586fc32020-07-30 14:22:13 +1000512 int node_noffset, const void *data, size_t size,
Philippe Reynes3148e422019-12-18 18:25:41 +0100513 const char *cmdname)
514{
515 struct image_cipher_info info;
516 unsigned char *data_ciphered = NULL;
517 int data_ciphered_len;
518 int ret;
519
520 memset(&info, 0, sizeof(info));
521
522 ret = fit_image_setup_cipher(&info, keydir, fit, image_name,
Patrick Oppenlanderd586fc32020-07-30 14:22:13 +1000523 image_noffset, node_noffset);
Philippe Reynes3148e422019-12-18 18:25:41 +0100524 if (ret)
525 goto out;
526
527 ret = info.cipher->encrypt(&info, data, size,
528 &data_ciphered, &data_ciphered_len);
529 if (ret)
530 goto out;
531
532 /*
533 * Write the public key into the supplied FDT file; this might fail
534 * several times, since we try signing with successively increasing
535 * size values
Philippe Reynes64d67c42020-09-17 15:01:46 +0200536 * And, if needed, write the iv in the FIT file
Philippe Reynes3148e422019-12-18 18:25:41 +0100537 */
538 if (keydest) {
Philippe Reynes64d67c42020-09-17 15:01:46 +0200539 ret = info.cipher->add_cipher_data(&info, keydest, fit, node_noffset);
Philippe Reynes3148e422019-12-18 18:25:41 +0100540 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300541 fprintf(stderr,
542 "Failed to add verification data for cipher '%s' in image '%s'\n",
543 info.keyname, image_name);
Philippe Reynes3148e422019-12-18 18:25:41 +0100544 goto out;
545 }
546 }
547
548 ret = fit_image_write_cipher(fit, image_noffset, node_noffset,
549 data, size,
550 data_ciphered, data_ciphered_len);
551
552 out:
553 free(data_ciphered);
554 free((void *)info.key);
555 free((void *)info.iv);
556 return ret;
557}
558
559int fit_image_cipher_data(const char *keydir, void *keydest,
560 void *fit, int image_noffset, const char *comment,
561 int require_keys, const char *engine_id,
562 const char *cmdname)
563{
564 const char *image_name;
565 const void *data;
566 size_t size;
Patrick Oppenlandera4fafb32020-07-30 14:22:15 +1000567 int cipher_node_offset, len;
Philippe Reynes3148e422019-12-18 18:25:41 +0100568
569 /* Get image name */
570 image_name = fit_get_name(fit, image_noffset, NULL);
571 if (!image_name) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300572 fprintf(stderr, "Can't get image name\n");
Philippe Reynes3148e422019-12-18 18:25:41 +0100573 return -1;
574 }
575
576 /* Get image data and data length */
577 if (fit_image_get_data(fit, image_noffset, &data, &size)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300578 fprintf(stderr, "Can't get image data/size\n");
Philippe Reynes3148e422019-12-18 18:25:41 +0100579 return -1;
580 }
581
Patrick Oppenlandera4fafb32020-07-30 14:22:15 +1000582 /*
583 * Don't cipher ciphered data.
584 *
585 * If the data-size-unciphered property is present the data for this
586 * image is already encrypted. This is important as 'mkimage -F' can be
587 * run multiple times on a FIT image.
588 */
589 if (fdt_getprop(fit, image_noffset, "data-size-unciphered", &len))
590 return 0;
591 if (len != -FDT_ERR_NOTFOUND) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300592 fprintf(stderr, "Failure testing for data-size-unciphered\n");
Patrick Oppenlandera4fafb32020-07-30 14:22:15 +1000593 return -1;
594 }
Philippe Reynes3148e422019-12-18 18:25:41 +0100595
Patrick Oppenlanderd586fc32020-07-30 14:22:13 +1000596 /* Process cipher node if present */
597 cipher_node_offset = fdt_subnode_offset(fit, image_noffset,
598 FIT_CIPHER_NODENAME);
599 if (cipher_node_offset == -FDT_ERR_NOTFOUND)
600 return 0;
601 if (cipher_node_offset < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300602 fprintf(stderr, "Failure getting cipher node\n");
Patrick Oppenlanderd586fc32020-07-30 14:22:13 +1000603 return -1;
Philippe Reynes3148e422019-12-18 18:25:41 +0100604 }
Patrick Oppenlanderd586fc32020-07-30 14:22:13 +1000605 if (!IMAGE_ENABLE_ENCRYPT || !keydir)
606 return 0;
607 return fit_image_process_cipher(keydir, keydest, fit, image_name,
608 image_noffset, cipher_node_offset, data, size, cmdname);
Philippe Reynes3148e422019-12-18 18:25:41 +0100609}
610
Simon Glassfbabc0f2013-06-13 15:10:01 -0700611/**
612 * fit_image_add_verification_data() - calculate/set verig. data for image node
613 *
614 * This adds hash and signature values for an component image node.
Simon Glassee382652013-05-07 06:12:01 +0000615 *
616 * All existing hash subnodes are checked, if algorithm property is set to
617 * one of the supported hash algorithms, hash value is computed and
618 * corresponding hash node property is set, for example:
Simon Glass10a1eca2013-05-07 06:11:54 +0000619 *
620 * Input component image node structure:
621 *
Andre Przywara3234ecd2017-12-04 02:05:10 +0000622 * o image-1 (at image_noffset)
Simon Glass10a1eca2013-05-07 06:11:54 +0000623 * | - data = [binary data]
Andre Przywara3234ecd2017-12-04 02:05:10 +0000624 * o hash-1
Simon Glass10a1eca2013-05-07 06:11:54 +0000625 * |- algo = "sha1"
626 *
627 * Output component image node structure:
628 *
Andre Przywara3234ecd2017-12-04 02:05:10 +0000629 * o image-1 (at image_noffset)
Simon Glass10a1eca2013-05-07 06:11:54 +0000630 * | - data = [binary data]
Andre Przywara3234ecd2017-12-04 02:05:10 +0000631 * o hash-1
Simon Glass10a1eca2013-05-07 06:11:54 +0000632 * |- algo = "sha1"
633 * |- value = sha1(data)
634 *
Simon Glassee382652013-05-07 06:12:01 +0000635 * For signature details, please see doc/uImage.FIT/signature.txt
636 *
Simon Glassfbabc0f2013-06-13 15:10:01 -0700637 * @keydir Directory containing *.key and *.crt files (or NULL)
638 * @keydest FDT Blob to write public keys into (NULL if none)
Simon Glassee382652013-05-07 06:12:01 +0000639 * @fit: Pointer to the FIT format image header
640 * @image_noffset: Requested component image node
Simon Glassfbabc0f2013-06-13 15:10:01 -0700641 * @comment: Comment to add to signature nodes
642 * @require_keys: Mark all keys as 'required'
George McCollister23d14892017-01-06 13:14:17 -0600643 * @engine_id: Engine to use for signing
Simon Glassee382652013-05-07 06:12:01 +0000644 * @return: 0 on success, <0 on failure
Simon Glass10a1eca2013-05-07 06:11:54 +0000645 */
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600646int fit_image_add_verification_data(const char *keydir, const char *keyfile,
647 void *keydest, void *fit, int image_noffset,
648 const char *comment, int require_keys, const char *engine_id,
Jan Kiszka4043f322022-01-14 10:21:19 +0100649 const char *cmdname, const char* algo_name)
Simon Glass10a1eca2013-05-07 06:11:54 +0000650{
Simon Glassee382652013-05-07 06:12:01 +0000651 const char *image_name;
Simon Glass10a1eca2013-05-07 06:11:54 +0000652 const void *data;
653 size_t size;
Simon Glass10a1eca2013-05-07 06:11:54 +0000654 int noffset;
Simon Glass10a1eca2013-05-07 06:11:54 +0000655
656 /* Get image data and data length */
657 if (fit_image_get_data(fit, image_noffset, &data, &size)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300658 fprintf(stderr, "Can't get image data/size\n");
Simon Glass10a1eca2013-05-07 06:11:54 +0000659 return -1;
660 }
661
Simon Glass25d6e6b2013-05-07 06:11:55 +0000662 image_name = fit_get_name(fit, image_noffset, NULL);
663
Simon Glass10a1eca2013-05-07 06:11:54 +0000664 /* Process all hash subnodes of the component image node */
Simon Glassee382652013-05-07 06:12:01 +0000665 for (noffset = fdt_first_subnode(fit, image_noffset);
666 noffset >= 0;
667 noffset = fdt_next_subnode(fit, noffset)) {
668 const char *node_name;
669 int ret = 0;
670
671 /*
672 * Check subnode name, must be equal to "hash" or "signature".
673 * Multiple hash nodes require unique unit node
Andre Przywara3234ecd2017-12-04 02:05:10 +0000674 * names, e.g. hash-1, hash-2, signature-1, etc.
Simon Glassee382652013-05-07 06:12:01 +0000675 */
676 node_name = fit_get_name(fit, noffset, NULL);
677 if (!strncmp(node_name, FIT_HASH_NODENAME,
678 strlen(FIT_HASH_NODENAME))) {
679 ret = fit_image_process_hash(fit, image_name, noffset,
680 data, size);
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600681 } else if (IMAGE_ENABLE_SIGN && (keydir || keyfile) &&
Simon Glassfbabc0f2013-06-13 15:10:01 -0700682 !strncmp(node_name, FIT_SIG_NODENAME,
683 strlen(FIT_SIG_NODENAME))) {
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -0600684 ret = fit_image_process_sig(keydir, keyfile, keydest,
Simon Glassfbabc0f2013-06-13 15:10:01 -0700685 fit, image_name, noffset, data, size,
Jan Kiszka4043f322022-01-14 10:21:19 +0100686 comment, require_keys, engine_id, cmdname,
687 algo_name);
Simon Glass10a1eca2013-05-07 06:11:54 +0000688 }
Simon Glass89c3fb62021-11-12 12:28:12 -0700689 if (ret < 0)
Simon Glassec539f72016-07-03 09:40:44 -0600690 return ret;
Simon Glassee382652013-05-07 06:12:01 +0000691 }
692
693 return 0;
694}
695
Simon Glass56ab8d62013-06-13 15:10:09 -0700696struct strlist {
697 int count;
698 char **strings;
699};
700
701static void strlist_init(struct strlist *list)
702{
703 memset(list, '\0', sizeof(*list));
704}
705
706static void strlist_free(struct strlist *list)
707{
708 int i;
709
710 for (i = 0; i < list->count; i++)
711 free(list->strings[i]);
712 free(list->strings);
713}
714
715static int strlist_add(struct strlist *list, const char *str)
716{
717 char *dup;
718
719 dup = strdup(str);
720 list->strings = realloc(list->strings,
721 (list->count + 1) * sizeof(char *));
722 if (!list || !str)
723 return -1;
724 list->strings[list->count++] = dup;
725
726 return 0;
727}
728
Simon Glass6a0efc82021-11-12 12:28:06 -0700729static const char *fit_config_get_image_list(const void *fit, int noffset,
730 int *lenp, int *allow_missingp)
Simon Glass56ab8d62013-06-13 15:10:09 -0700731{
732 static const char default_list[] = FIT_KERNEL_PROP "\0"
Alexander Dahl907ee6a2024-06-20 16:20:59 +0200733 FIT_FDT_PROP "\0" FIT_SCRIPT_PROP;
Simon Glass56ab8d62013-06-13 15:10:09 -0700734 const char *prop;
735
Simon Glass6a0efc82021-11-12 12:28:06 -0700736 /* If there is an "sign-image" property, use that */
Simon Glass56ab8d62013-06-13 15:10:09 -0700737 prop = fdt_getprop(fit, noffset, "sign-images", lenp);
738 if (prop) {
739 *allow_missingp = 0;
740 return *lenp ? prop : NULL;
741 }
742
743 /* Default image list */
744 *allow_missingp = 1;
745 *lenp = sizeof(default_list);
746
747 return default_list;
748}
749
Simon Glass6a0efc82021-11-12 12:28:06 -0700750/**
751 * fit_config_add_hash() - Add a list of nodes to hash for an image
752 *
753 * This adds a list of paths to image nodes (as referred to by a particular
754 * offset) that need to be hashed, to protect a configuration
755 *
756 * @fit: Pointer to the FIT format image header
757 * @image_noffset: Offset of image to process (e.g. /images/kernel-1)
758 * @node_inc: List of nodes to add to
759 * @conf_name Configuration-node name, child of /configurations node (only
760 * used for error messages)
761 * @sig_name Signature-node name (only used for error messages)
762 * @iname: Name of image being processed (e.g. "kernel-1" (only used
763 * for error messages)
764 */
765static int fit_config_add_hash(const void *fit, int image_noffset,
766 struct strlist *node_inc, const char *conf_name,
767 const char *sig_name, const char *iname)
Philippe Reynes856bcc82020-11-24 14:39:47 +0100768{
Simon Glass888c0c52021-11-12 12:28:07 -0700769 char path[200];
Philippe Reynes856bcc82020-11-24 14:39:47 +0100770 int noffset;
771 int hash_count;
772 int ret;
773
774 ret = fdt_get_path(fit, image_noffset, path, sizeof(path));
775 if (ret < 0)
776 goto err_path;
777 if (strlist_add(node_inc, path))
778 goto err_mem;
779
Philippe Reynes856bcc82020-11-24 14:39:47 +0100780 /* Add all this image's hashes */
781 hash_count = 0;
782 for (noffset = fdt_first_subnode(fit, image_noffset);
783 noffset >= 0;
784 noffset = fdt_next_subnode(fit, noffset)) {
785 const char *name = fit_get_name(fit, noffset, NULL);
786
787 if (strncmp(name, FIT_HASH_NODENAME,
788 strlen(FIT_HASH_NODENAME)))
789 continue;
790 ret = fdt_get_path(fit, noffset, path, sizeof(path));
791 if (ret < 0)
792 goto err_path;
793 if (strlist_add(node_inc, path))
794 goto err_mem;
795 hash_count++;
796 }
797
798 if (!hash_count) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300799 fprintf(stderr,
800 "Failed to find any hash nodes in configuration '%s/%s' image '%s' - without these it is not possible to verify this image\n",
801 conf_name, sig_name, iname);
Philippe Reynes856bcc82020-11-24 14:39:47 +0100802 return -ENOMSG;
803 }
804
805 /* Add this image's cipher node if present */
806 noffset = fdt_subnode_offset(fit, image_noffset,
807 FIT_CIPHER_NODENAME);
808 if (noffset != -FDT_ERR_NOTFOUND) {
809 if (noffset < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300810 fprintf(stderr,
811 "Failed to get cipher node in configuration '%s/%s' image '%s': %s\n",
812 conf_name, sig_name, iname,
813 fdt_strerror(noffset));
Philippe Reynes856bcc82020-11-24 14:39:47 +0100814 return -EIO;
815 }
816 ret = fdt_get_path(fit, noffset, path, sizeof(path));
817 if (ret < 0)
818 goto err_path;
819 if (strlist_add(node_inc, path))
820 goto err_mem;
821 }
822
823 return 0;
824
825err_mem:
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300826 fprintf(stderr, "Out of memory processing configuration '%s/%s'\n", conf_name,
827 sig_name);
Philippe Reynes856bcc82020-11-24 14:39:47 +0100828 return -ENOMEM;
829
830err_path:
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300831 fprintf(stderr, "Failed to get path for image '%s' in configuration '%s/%s': %s\n",
832 iname, conf_name, sig_name, fdt_strerror(ret));
Philippe Reynes856bcc82020-11-24 14:39:47 +0100833 return -ENOENT;
834}
835
Simon Glass6a0efc82021-11-12 12:28:06 -0700836/**
837 * fit_config_get_hash_list() - Get the regions to sign
838 *
839 * This calculates a list of nodes to hash for this particular configuration,
840 * returning it as a string list (struct strlist, not a devicetree string list)
841 *
842 * @fit: Pointer to the FIT format image header
843 * @conf_noffset: Offset of configuration node to sign (child of
844 * /configurations node)
845 * @sig_offset: Offset of signature node containing info about how to sign it
846 * (child of 'signatures' node)
847 * @return 0 if OK, -ENOENT if an image referred to by the configuration cannot
848 * be found, -ENOMSG if ther were no images in the configuration
849 */
850static int fit_config_get_hash_list(const void *fit, int conf_noffset,
Simon Glass56ab8d62013-06-13 15:10:09 -0700851 int sig_offset, struct strlist *node_inc)
852{
853 int allow_missing;
854 const char *prop, *iname, *end;
855 const char *conf_name, *sig_name;
Philippe Reynes856bcc82020-11-24 14:39:47 +0100856 char name[200];
Simon Glass56ab8d62013-06-13 15:10:09 -0700857 int image_count;
858 int ret, len;
859
860 conf_name = fit_get_name(fit, conf_noffset, NULL);
861 sig_name = fit_get_name(fit, sig_offset, NULL);
862
863 /*
864 * Build a list of nodes we need to hash. We always need the root
865 * node and the configuration.
866 */
867 strlist_init(node_inc);
868 snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, conf_name);
869 if (strlist_add(node_inc, "/") ||
870 strlist_add(node_inc, name))
871 goto err_mem;
872
873 /* Get a list of images that we intend to sign */
Heiko Schocherfdfd5202014-03-03 12:19:23 +0100874 prop = fit_config_get_image_list(fit, sig_offset, &len,
Simon Glass56ab8d62013-06-13 15:10:09 -0700875 &allow_missing);
876 if (!prop)
877 return 0;
878
879 /* Locate the images */
880 end = prop + len;
881 image_count = 0;
882 for (iname = prop; iname < end; iname += strlen(iname) + 1) {
Simon Glass56ab8d62013-06-13 15:10:09 -0700883 int image_noffset;
Philippe Reynes1f7d9a22020-11-24 14:39:48 +0100884 int index, max_index;
Simon Glass56ab8d62013-06-13 15:10:09 -0700885
Philippe Reynes1f7d9a22020-11-24 14:39:48 +0100886 max_index = fdt_stringlist_count(fit, conf_noffset, iname);
Simon Glass56ab8d62013-06-13 15:10:09 -0700887
Philippe Reynes1f7d9a22020-11-24 14:39:48 +0100888 for (index = 0; index < max_index; index++) {
889 image_noffset = fit_conf_get_prop_node_index(fit, conf_noffset,
890 iname, index);
891
892 if (image_noffset < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300893 fprintf(stderr,
894 "Failed to find image '%s' in configuration '%s/%s'\n",
895 iname, conf_name, sig_name);
Philippe Reynes1f7d9a22020-11-24 14:39:48 +0100896 if (allow_missing)
897 continue;
Simon Glass56ab8d62013-06-13 15:10:09 -0700898
Philippe Reynes1f7d9a22020-11-24 14:39:48 +0100899 return -ENOENT;
900 }
Patrick Oppenlander5964c3f2020-07-30 14:30:47 +1000901
Simon Glass6a0efc82021-11-12 12:28:06 -0700902 ret = fit_config_add_hash(fit, image_noffset, node_inc,
903 conf_name, sig_name, iname);
Philippe Reynes1f7d9a22020-11-24 14:39:48 +0100904 if (ret < 0)
905 return ret;
906
907 image_count++;
908 }
Simon Glass56ab8d62013-06-13 15:10:09 -0700909 }
910
911 if (!image_count) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300912 fprintf(stderr, "Failed to find any images for configuration '%s/%s'\n",
913 conf_name, sig_name);
Simon Glass56ab8d62013-06-13 15:10:09 -0700914 return -ENOMSG;
915 }
916
917 return 0;
918
919err_mem:
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300920 fprintf(stderr, "Out of memory processing configuration '%s/%s'\n", conf_name,
921 sig_name);
Simon Glass56ab8d62013-06-13 15:10:09 -0700922 return -ENOMEM;
Simon Glass56ab8d62013-06-13 15:10:09 -0700923}
924
Simon Glass6a0efc82021-11-12 12:28:06 -0700925/**
926 * fit_config_get_regions() - Get the regions to sign
927 *
928 * This calculates a list of node to hash for this particular configuration,
929 * then finds which regions of the devicetree they correspond to.
930 *
931 * @fit: Pointer to the FIT format image header
932 * @conf_noffset: Offset of configuration node to sign (child of
933 * /configurations node)
934 * @sig_offset: Offset of signature node containing info about how to sign it
935 * (child of 'signatures' node)
936 * @regionp: Returns list of regions that need to be hashed (allocated; must be
937 * freed by the caller)
938 * @region_count: Returns number of regions
939 * @region_propp: Returns string-list property containing the list of nodes
940 * that correspond to the regions. Each entry is a full path to the node.
941 * This is in devicetree format, i.e. a \0 between each string. This is
942 * allocated and must be freed by the caller.
943 * @region_proplen: Returns length of *@@region_propp in bytes
944 * @return 0 if OK, -ENOMEM if out of memory, -EIO if the regions to hash could
945 * not be found, -EINVAL if no registers were found to hash
946 */
947static int fit_config_get_regions(const void *fit, int conf_noffset,
948 int sig_offset, struct image_region **regionp,
949 int *region_countp, char **region_propp,
950 int *region_proplen)
Simon Glass56ab8d62013-06-13 15:10:09 -0700951{
Sean Anderson016f4d52022-10-20 15:41:10 -0400952 char * const exc_prop[] = {
953 FIT_DATA_PROP,
954 FIT_DATA_SIZE_PROP,
955 FIT_DATA_POSITION_PROP,
956 FIT_DATA_OFFSET_PROP,
957 };
Simon Glass56ab8d62013-06-13 15:10:09 -0700958 struct strlist node_inc;
959 struct image_region *region;
960 struct fdt_region fdt_regions[100];
961 const char *conf_name, *sig_name;
962 char path[200];
963 int count, i;
964 char *region_prop;
965 int ret, len;
966
967 conf_name = fit_get_name(fit, conf_noffset, NULL);
Simon Glass6a0efc82021-11-12 12:28:06 -0700968 sig_name = fit_get_name(fit, sig_offset, NULL);
Simon Glass56ab8d62013-06-13 15:10:09 -0700969 debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name);
970
971 /* Get a list of nodes we want to hash */
Simon Glass6a0efc82021-11-12 12:28:06 -0700972 ret = fit_config_get_hash_list(fit, conf_noffset, sig_offset,
973 &node_inc);
Simon Glass56ab8d62013-06-13 15:10:09 -0700974 if (ret)
975 return ret;
976
977 /* Get a list of regions to hash */
978 count = fdt_find_regions(fit, node_inc.strings, node_inc.count,
979 exc_prop, ARRAY_SIZE(exc_prop),
980 fdt_regions, ARRAY_SIZE(fdt_regions),
981 path, sizeof(path), 1);
982 if (count < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300983 fprintf(stderr, "Failed to hash configuration '%s/%s': %s\n", conf_name,
984 sig_name, fdt_strerror(ret));
Simon Glass56ab8d62013-06-13 15:10:09 -0700985 return -EIO;
986 }
987 if (count == 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300988 fprintf(stderr, "No data to hash for configuration '%s/%s': %s\n",
989 conf_name, sig_name, fdt_strerror(ret));
Simon Glass56ab8d62013-06-13 15:10:09 -0700990 return -EINVAL;
991 }
992
993 /* Build our list of data blocks */
994 region = fit_region_make_list(fit, fdt_regions, count, NULL);
995 if (!region) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +0300996 fprintf(stderr, "Out of memory hashing configuration '%s/%s'\n",
997 conf_name, sig_name);
Simon Glass56ab8d62013-06-13 15:10:09 -0700998 return -ENOMEM;
999 }
1000
1001 /* Create a list of all hashed properties */
1002 debug("Hash nodes:\n");
1003 for (i = len = 0; i < node_inc.count; i++) {
1004 debug(" %s\n", node_inc.strings[i]);
1005 len += strlen(node_inc.strings[i]) + 1;
1006 }
1007 region_prop = malloc(len);
1008 if (!region_prop) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001009 fprintf(stderr, "Out of memory setting up regions for configuration '%s/%s'\n",
1010 conf_name, sig_name);
Simon Glass56ab8d62013-06-13 15:10:09 -07001011 return -ENOMEM;
1012 }
1013 for (i = len = 0; i < node_inc.count;
1014 len += strlen(node_inc.strings[i]) + 1, i++)
1015 strcpy(region_prop + len, node_inc.strings[i]);
1016 strlist_free(&node_inc);
1017
1018 *region_countp = count;
1019 *regionp = region;
1020 *region_propp = region_prop;
1021 *region_proplen = len;
1022
1023 return 0;
1024}
1025
Simon Glass89c3fb62021-11-12 12:28:12 -07001026/**
1027 * fit_config_process_sig - Process a single subnode of the configurations/ node
1028 *
1029 * Generate a signed hash of the supplied data and store it in the node.
1030 *
1031 * @keydir: Directory containing keys to use for signing
1032 * @keydest: Destination FDT blob to write public keys into (NULL if none)
1033 * @fit: pointer to the FIT format image header
1034 * @conf_name name of config being processed (used to display errors)
1035 * @conf_noffset: Offset of configuration node, e.g. '/configurations/conf-1'
1036 * @noffset: subnode offset, e.g. '/configurations/conf-1/sig-1'
1037 * @comment: Comment to add to signature nodes
1038 * @require_keys: Mark all keys as 'required'
1039 * @engine_id: Engine to use for signing
1040 * @cmdname: Command name used when reporting errors
1041 * @return keydest node if @keydest is non-NULL, else 0 if none; -ve error code
1042 * on failure
1043 */
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001044static int fit_config_process_sig(const char *keydir, const char *keyfile,
Simon Glass6a0efc82021-11-12 12:28:06 -07001045 void *keydest, void *fit, const char *conf_name,
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001046 int conf_noffset, int noffset, const char *comment,
Jan Kiszka4043f322022-01-14 10:21:19 +01001047 int require_keys, const char *engine_id, const char *cmdname,
1048 const char *algo_name)
Simon Glass56ab8d62013-06-13 15:10:09 -07001049{
1050 struct image_sign_info info;
1051 const char *node_name;
1052 struct image_region *region;
1053 char *region_prop;
1054 int region_proplen;
1055 int region_count;
1056 uint8_t *value;
1057 uint value_len;
1058 int ret;
1059
1060 node_name = fit_get_name(fit, noffset, NULL);
Simon Glass6a0efc82021-11-12 12:28:06 -07001061 if (fit_config_get_regions(fit, conf_noffset, noffset, &region,
1062 &region_count, &region_prop,
1063 &region_proplen))
Simon Glass56ab8d62013-06-13 15:10:09 -07001064 return -1;
1065
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001066 if (fit_image_setup_sig(&info, keydir, keyfile, fit, conf_name, noffset,
Jan Kiszka4043f322022-01-14 10:21:19 +01001067 require_keys ? "conf" : NULL, engine_id,
1068 algo_name))
Simon Glass56ab8d62013-06-13 15:10:09 -07001069 return -1;
1070
Andrew Duda6616c822016-11-08 18:53:41 +00001071 ret = info.crypto->sign(&info, region, region_count, &value,
1072 &value_len);
Simon Glass56ab8d62013-06-13 15:10:09 -07001073 free(region);
1074 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001075 fprintf(stderr, "Failed to sign '%s' signature node in '%s' conf node\n",
1076 node_name, conf_name);
Simon Glass56ab8d62013-06-13 15:10:09 -07001077
1078 /* We allow keys to be missing */
1079 if (ret == -ENOENT)
1080 return 0;
1081 return -1;
1082 }
1083
Simon Glass802aa822014-06-02 22:04:53 -06001084 ret = fit_image_write_sig(fit, noffset, value, value_len, comment,
Jan Kiszka4043f322022-01-14 10:21:19 +01001085 region_prop, region_proplen, cmdname,
1086 algo_name);
Simon Glass802aa822014-06-02 22:04:53 -06001087 if (ret) {
1088 if (ret == -FDT_ERR_NOSPACE)
1089 return -ENOSPC;
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001090 fprintf(stderr,
1091 "Can't write signature for '%s' signature node in '%s' conf node: %s\n",
1092 node_name, conf_name, fdt_strerror(ret));
Simon Glass56ab8d62013-06-13 15:10:09 -07001093 return -1;
1094 }
1095 free(value);
1096 free(region_prop);
1097
1098 /* Get keyname again, as FDT has changed and invalidated our pointer */
Simon Glassd7aabcc2020-03-18 11:44:06 -06001099 info.keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL);
Simon Glass56ab8d62013-06-13 15:10:09 -07001100
1101 /* Write the public key into the supplied FDT file */
Simon Glass802aa822014-06-02 22:04:53 -06001102 if (keydest) {
Andrew Duda6616c822016-11-08 18:53:41 +00001103 ret = info.crypto->add_verify_data(&info, keydest);
Simon Glass94336dc2021-11-12 12:28:11 -07001104 if (ret < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001105 fprintf(stderr,
1106 "Failed to add verification data for '%s' signature node in '%s' configuration node\n",
1107 node_name, conf_name);
Simon Glass802aa822014-06-02 22:04:53 -06001108 }
Simon Glass89c3fb62021-11-12 12:28:12 -07001109 return ret;
Simon Glass56ab8d62013-06-13 15:10:09 -07001110 }
1111
1112 return 0;
1113}
1114
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001115static int fit_config_add_verification_data(const char *keydir,
1116 const char *keyfile, void *keydest, void *fit, int conf_noffset,
1117 const char *comment, int require_keys, const char *engine_id,
Simon Glasse4607262021-11-12 12:28:13 -07001118 const char *cmdname, const char *algo_name,
1119 struct image_summary *summary)
Simon Glass56ab8d62013-06-13 15:10:09 -07001120{
1121 const char *conf_name;
1122 int noffset;
1123
1124 conf_name = fit_get_name(fit, conf_noffset, NULL);
1125
1126 /* Process all hash subnodes of the configuration node */
1127 for (noffset = fdt_first_subnode(fit, conf_noffset);
1128 noffset >= 0;
1129 noffset = fdt_next_subnode(fit, noffset)) {
1130 const char *node_name;
1131 int ret = 0;
1132
1133 node_name = fit_get_name(fit, noffset, NULL);
1134 if (!strncmp(node_name, FIT_SIG_NODENAME,
1135 strlen(FIT_SIG_NODENAME))) {
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001136 ret = fit_config_process_sig(keydir, keyfile, keydest,
Simon Glass56ab8d62013-06-13 15:10:09 -07001137 fit, conf_name, conf_noffset, noffset, comment,
Jan Kiszka4043f322022-01-14 10:21:19 +01001138 require_keys, engine_id, cmdname, algo_name);
Simon Glasse4607262021-11-12 12:28:13 -07001139 if (ret < 0)
1140 return ret;
1141
1142 summary->sig_offset = noffset;
1143 fdt_get_path(fit, noffset, summary->sig_path,
1144 sizeof(summary->sig_path));
1145
1146 if (keydest) {
1147 summary->keydest_offset = ret;
1148 fdt_get_path(keydest, ret,
1149 summary->keydest_path,
1150 sizeof(summary->keydest_path));
1151 }
Simon Glass56ab8d62013-06-13 15:10:09 -07001152 }
Simon Glass56ab8d62013-06-13 15:10:09 -07001153 }
1154
1155 return 0;
1156}
1157
Paul-Erwan Riodcfb6332023-12-21 08:26:11 +01001158#if CONFIG_IS_ENABLED(FIT_SIGNATURE)
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001159/*
1160 * 0) open file (open)
1161 * 1) read certificate (PEM_read_X509)
1162 * 2) get public key (X509_get_pubkey)
1163 * 3) provide der format (d2i_RSAPublicKey)
1164 */
1165static int read_pub_key(const char *keydir, const void *name,
1166 unsigned char **pubkey, int *pubkey_len)
1167{
1168 char path[1024];
1169 EVP_PKEY *key = NULL;
1170 X509 *cert;
1171 FILE *f;
1172 int ret;
1173
1174 memset(path, 0, 1024);
1175 snprintf(path, sizeof(path), "%s/%s.crt", keydir, (char *)name);
1176
1177 /* Open certificate file */
1178 f = fopen(path, "r");
1179 if (!f) {
1180 fprintf(stderr, "Couldn't open RSA certificate: '%s': %s\n",
1181 path, strerror(errno));
1182 return -EACCES;
1183 }
1184
1185 /* Read the certificate */
1186 cert = NULL;
1187 if (!PEM_read_X509(f, &cert, NULL, NULL)) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001188 fprintf(stderr, "Couldn't read certificate");
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001189 ret = -EINVAL;
1190 goto err_cert;
1191 }
1192
1193 /* Get the public key from the certificate. */
1194 key = X509_get_pubkey(cert);
1195 if (!key) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001196 fprintf(stderr, "Couldn't read public key\n");
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001197 ret = -EINVAL;
1198 goto err_pubkey;
1199 }
1200
1201 /* Get DER form */
1202 ret = i2d_PublicKey(key, pubkey);
1203 if (ret < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001204 fprintf(stderr, "Couldn't get DER form\n");
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001205 ret = -EINVAL;
1206 goto err_pubkey;
1207 }
1208
1209 *pubkey_len = ret;
1210 ret = 0;
1211
1212err_pubkey:
1213 X509_free(cert);
1214err_cert:
1215 fclose(f);
1216 return ret;
1217}
1218
1219int fit_pre_load_data(const char *keydir, void *keydest, void *fit)
1220{
1221 int pre_load_noffset;
1222 const void *algo_name;
1223 const void *key_name;
1224 unsigned char *pubkey = NULL;
1225 int ret, pubkey_len;
1226
1227 if (!keydir || !keydest || !fit)
1228 return 0;
1229
1230 /* Search node pre-load sig */
1231 pre_load_noffset = fdt_path_offset(keydest, IMAGE_PRE_LOAD_PATH);
1232 if (pre_load_noffset < 0) {
1233 ret = 0;
1234 goto out;
1235 }
1236
1237 algo_name = fdt_getprop(keydest, pre_load_noffset, "algo-name", NULL);
1238 key_name = fdt_getprop(keydest, pre_load_noffset, "key-name", NULL);
1239
1240 /* Check that all mandatory properties are present */
1241 if (!algo_name || !key_name) {
1242 if (!algo_name)
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001243 fprintf(stderr, "The property algo-name is missing in the node %s\n",
1244 IMAGE_PRE_LOAD_PATH);
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001245 if (!key_name)
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001246 fprintf(stderr, "The property key-name is missing in the node %s\n",
1247 IMAGE_PRE_LOAD_PATH);
Mark Kettenisd4ba0b42022-04-26 19:24:38 +02001248 ret = -EINVAL;
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001249 goto out;
1250 }
1251
1252 /* Read public key */
1253 ret = read_pub_key(keydir, key_name, &pubkey, &pubkey_len);
1254 if (ret < 0)
1255 goto out;
1256
1257 /* Add the public key to the device tree */
1258 ret = fdt_setprop(keydest, pre_load_noffset, "public-key",
1259 pubkey, pubkey_len);
1260 if (ret)
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001261 fprintf(stderr, "Can't set public-key in node %s (ret = %d)\n",
1262 IMAGE_PRE_LOAD_PATH, ret);
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001263
1264 out:
1265 return ret;
1266}
Paul-Erwan Riodcfb6332023-12-21 08:26:11 +01001267#endif
Philippe Reynes3e3899c2022-03-28 22:57:02 +02001268
Philippe Reynes3148e422019-12-18 18:25:41 +01001269int fit_cipher_data(const char *keydir, void *keydest, void *fit,
1270 const char *comment, int require_keys,
1271 const char *engine_id, const char *cmdname)
1272{
1273 int images_noffset;
1274 int noffset;
1275 int ret;
1276
1277 /* Find images parent node offset */
1278 images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
1279 if (images_noffset < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001280 fprintf(stderr, "Can't find images parent node '%s' (%s)\n",
1281 FIT_IMAGES_PATH, fdt_strerror(images_noffset));
Philippe Reynes3148e422019-12-18 18:25:41 +01001282 return images_noffset;
1283 }
1284
1285 /* Process its subnodes, print out component images details */
1286 for (noffset = fdt_first_subnode(fit, images_noffset);
1287 noffset >= 0;
1288 noffset = fdt_next_subnode(fit, noffset)) {
1289 /*
1290 * Direct child node of the images parent node,
1291 * i.e. component image node.
1292 */
1293 ret = fit_image_cipher_data(keydir, keydest,
1294 fit, noffset, comment,
1295 require_keys, engine_id,
1296 cmdname);
1297 if (ret)
1298 return ret;
1299 }
1300
1301 return 0;
1302}
1303
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001304int fit_add_verification_data(const char *keydir, const char *keyfile,
1305 void *keydest, void *fit, const char *comment,
1306 int require_keys, const char *engine_id,
Simon Glasse4607262021-11-12 12:28:13 -07001307 const char *cmdname, const char *algo_name,
1308 struct image_summary *summary)
Simon Glassee382652013-05-07 06:12:01 +00001309{
Simon Glass56ab8d62013-06-13 15:10:09 -07001310 int images_noffset, confs_noffset;
Simon Glassee382652013-05-07 06:12:01 +00001311 int noffset;
1312 int ret;
1313
1314 /* Find images parent node offset */
1315 images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
1316 if (images_noffset < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001317 fprintf(stderr, "Can't find images parent node '%s' (%s)\n",
1318 FIT_IMAGES_PATH, fdt_strerror(images_noffset));
Simon Glassee382652013-05-07 06:12:01 +00001319 return images_noffset;
1320 }
1321
1322 /* Process its subnodes, print out component images details */
1323 for (noffset = fdt_first_subnode(fit, images_noffset);
1324 noffset >= 0;
1325 noffset = fdt_next_subnode(fit, noffset)) {
1326 /*
1327 * Direct child node of the images parent node,
1328 * i.e. component image node.
1329 */
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001330 ret = fit_image_add_verification_data(keydir, keyfile, keydest,
Alex Kiernan697fcdc2018-06-20 20:10:52 +00001331 fit, noffset, comment, require_keys, engine_id,
Jan Kiszka4043f322022-01-14 10:21:19 +01001332 cmdname, algo_name);
Simon Glass35414072022-12-21 16:08:23 -07001333 if (ret) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001334 fprintf(stderr, "Can't add verification data for node '%s' (%s)\n",
1335 fdt_get_name(fit, noffset, NULL),
Alexander Kochetkov197ee8b2024-09-16 11:24:45 +03001336 strerror(-ret));
Simon Glassee382652013-05-07 06:12:01 +00001337 return ret;
Simon Glass35414072022-12-21 16:08:23 -07001338 }
Simon Glass10a1eca2013-05-07 06:11:54 +00001339 }
1340
Simon Glass56ab8d62013-06-13 15:10:09 -07001341 /* If there are no keys, we can't sign configurations */
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001342 if (!IMAGE_ENABLE_SIGN || !(keydir || keyfile))
Simon Glass56ab8d62013-06-13 15:10:09 -07001343 return 0;
1344
1345 /* Find configurations parent node offset */
1346 confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
1347 if (confs_noffset < 0) {
Oleksandr Suvorov9ed732d2023-08-17 18:36:10 +03001348 fprintf(stderr, "Can't find images parent node '%s' (%s)\n",
1349 FIT_CONFS_PATH, fdt_strerror(confs_noffset));
Simon Glass56ab8d62013-06-13 15:10:09 -07001350 return -ENOENT;
1351 }
1352
1353 /* Process its subnodes, print out component images details */
1354 for (noffset = fdt_first_subnode(fit, confs_noffset);
1355 noffset >= 0;
1356 noffset = fdt_next_subnode(fit, noffset)) {
Alexandru Gagniuc8fcea122021-02-19 12:45:17 -06001357 ret = fit_config_add_verification_data(keydir, keyfile, keydest,
Simon Glass56ab8d62013-06-13 15:10:09 -07001358 fit, noffset, comment,
George McCollister23d14892017-01-06 13:14:17 -06001359 require_keys,
Jan Kiszka4043f322022-01-14 10:21:19 +01001360 engine_id, cmdname,
Simon Glasse4607262021-11-12 12:28:13 -07001361 algo_name, summary);
Simon Glass56ab8d62013-06-13 15:10:09 -07001362 if (ret)
1363 return ret;
1364 }
1365
Simon Glass10a1eca2013-05-07 06:11:54 +00001366 return 0;
1367}
Heiko Schocherd7b42322014-03-03 12:19:30 +01001368
1369#ifdef CONFIG_FIT_SIGNATURE
Simon Glass05712322020-03-18 11:44:03 -06001370int fit_check_sign(const void *fit, const void *key,
1371 const char *fit_uname_config)
Heiko Schocherd7b42322014-03-03 12:19:30 +01001372{
1373 int cfg_noffset;
1374 int ret;
1375
Simon Glass05712322020-03-18 11:44:03 -06001376 cfg_noffset = fit_conf_get_node(fit, fit_uname_config);
Heiko Schocherd7b42322014-03-03 12:19:30 +01001377 if (!cfg_noffset)
1378 return -1;
1379
Simon Glassa559bb22020-03-18 11:43:56 -06001380 printf("Verifying Hash Integrity for node '%s'... ",
1381 fdt_get_name(fit, cfg_noffset, NULL));
Simon Glassa51991d2014-06-12 07:24:53 -06001382 ret = fit_config_verify(fit, cfg_noffset);
1383 if (ret)
1384 return ret;
Simon Glass05712322020-03-18 11:44:03 -06001385 printf("Verified OK, loading images\n");
Simon Glassa51991d2014-06-12 07:24:53 -06001386 ret = bootm_host_load_images(fit, cfg_noffset);
1387
Heiko Schocherd7b42322014-03-03 12:19:30 +01001388 return ret;
1389}
1390#endif