Coverage Report

Created: 2025-08-26 06:55

/src/fwupd/libfwupdplugin/fu-fit-firmware.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
287
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-bytes.h"
12
#include "fu-crc.h"
13
#include "fu-dump.h"
14
#include "fu-fdt-image.h"
15
#include "fu-fit-firmware.h"
16
#include "fu-input-stream.h"
17
#include "fu-mem.h"
18
19
/**
20
 * FuFitFirmware:
21
 *
22
 * A Flat Image Tree.
23
 *
24
 * Documented:
25
 * https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt
26
 *
27
 * See also: [class@FuFdtFirmware]
28
 */
29
30
G_DEFINE_TYPE(FuFitFirmware, fu_fit_firmware, FU_TYPE_FDT_FIRMWARE)
31
32
static FuFdtImage *
33
fu_fit_firmware_get_image_root(FuFitFirmware *self)
34
0
{
35
0
  FuFirmware *img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), NULL, NULL);
36
0
  if (img != NULL)
37
0
    return FU_FDT_IMAGE(img);
38
0
  img = fu_fdt_image_new();
39
0
  fu_fdt_image_set_attr_uint32(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_TIMESTAMP, 0x0);
40
0
  fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "description", "Firmware image");
41
0
  fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "creator", "fwupd");
42
0
  fu_firmware_add_image(FU_FIRMWARE(self), img);
43
0
  return FU_FDT_IMAGE(img);
44
0
}
45
46
/**
47
 * fu_fit_firmware_get_timestamp:
48
 * @self: a #FuFitFirmware
49
 *
50
 * Gets the creation timestamp.
51
 *
52
 * Returns: integer
53
 *
54
 * Since: 1.8.2
55
 **/
56
guint32
57
fu_fit_firmware_get_timestamp(FuFitFirmware *self)
58
0
{
59
0
  guint32 tmp = 0;
60
0
  g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self);
61
62
0
  g_return_val_if_fail(FU_IS_FIT_FIRMWARE(self), 0x0);
63
64
  /* this has to exist */
65
0
  (void)fu_fdt_image_get_attr_u32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, &tmp, NULL);
66
0
  return tmp;
67
0
}
68
69
/**
70
 * fu_fit_firmware_set_timestamp:
71
 * @self: a #FuFitFirmware
72
 * @timestamp: integer value
73
 *
74
 * Sets the creation timestamp.
75
 *
76
 * Since: 1.8.2
77
 **/
78
void
79
fu_fit_firmware_set_timestamp(FuFitFirmware *self, guint32 timestamp)
80
0
{
81
0
  g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self);
82
0
  g_return_if_fail(FU_IS_FIT_FIRMWARE(self));
83
0
  fu_fdt_image_set_attr_uint32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, timestamp);
84
0
}
85
86
static gboolean
87
fu_fit_firmware_verify_crc32(FuFirmware *firmware,
88
           FuFirmware *img,
89
           FuFirmware *img_hash,
90
           GBytes *blob,
91
           GError **error)
92
135
{
93
135
  guint32 value = 0;
94
135
  guint32 value_calc;
95
96
  /* get value and verify */
97
135
  if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img_hash),
98
135
               FU_FIT_FIRMWARE_ATTR_VALUE,
99
135
               &value,
100
135
               error))
101
1
    return FALSE;
102
134
  value_calc = fu_crc32_bytes(FU_CRC_KIND_B32_STANDARD, blob);
103
134
  if (value_calc != value) {
104
79
    g_set_error(error,
105
79
          FWUPD_ERROR,
106
79
          FWUPD_ERROR_INVALID_DATA,
107
79
          "%s CRC did not match, got 0x%x, expected 0x%x",
108
79
          fu_firmware_get_id(img),
109
79
          value,
110
79
          value_calc);
111
79
    return FALSE;
112
79
  }
113
114
  /* success */
115
55
  fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM);
116
55
  return TRUE;
117
134
}
118
119
static gboolean
120
fu_fit_firmware_verify_checksum(FuFirmware *firmware,
121
        FuFirmware *img,
122
        FuFirmware *img_hash,
123
        GChecksumType checksum_type,
124
        GBytes *blob,
125
        GError **error)
126
9
{
127
9
  gsize digest_len = g_checksum_type_get_length(checksum_type);
128
9
  g_autofree guint8 *buf = g_malloc0(digest_len);
129
9
  g_autoptr(GBytes) value = NULL;
130
9
  g_autoptr(GBytes) value_calc = NULL;
131
9
  g_autoptr(GChecksum) checksum = g_checksum_new(checksum_type);
132
133
  /* get value and verify */
134
9
  value = fu_fdt_image_get_attr(FU_FDT_IMAGE(img_hash), FU_FIT_FIRMWARE_ATTR_VALUE, error);
135
9
  if (value == NULL)
136
4
    return FALSE;
137
5
  if (g_bytes_get_size(value) != digest_len) {
138
5
    g_set_error(error,
139
5
          FWUPD_ERROR,
140
5
          FWUPD_ERROR_INVALID_DATA,
141
5
          "%s invalid hash value size, got 0x%x, expected 0x%x",
142
5
          fu_firmware_get_id(img),
143
5
          (guint)g_bytes_get_size(value),
144
5
          (guint)digest_len);
145
5
    return FALSE;
146
5
  }
147
0
  g_checksum_update(checksum,
148
0
        (const guchar *)g_bytes_get_data(value, NULL),
149
0
        g_bytes_get_size(value));
150
0
  g_checksum_get_digest(checksum, buf, &digest_len);
151
0
  value_calc = g_bytes_new(buf, digest_len);
152
0
  if (!fu_bytes_compare(value, value_calc, error))
153
0
    return FALSE;
154
155
  /* success */
156
0
  fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM);
157
0
  return TRUE;
158
0
}
159
160
static gboolean
161
fu_fit_firmware_verify_hash(FuFirmware *firmware,
162
          FuFirmware *img,
163
          FuFirmware *img_hash,
164
          GBytes *blob,
165
          GError **error)
166
295
{
167
295
  g_autofree gchar *algo = NULL;
168
169
  /* what is this */
170
295
  if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img_hash),
171
295
               FU_FIT_FIRMWARE_ATTR_ALGO,
172
295
               &algo,
173
295
               error)) {
174
10
    g_prefix_error(error, "cannot get algo for %s: ", fu_firmware_get_id(img));
175
10
    return FALSE;
176
10
  }
177
285
  if (g_strcmp0(algo, "crc32") == 0)
178
135
    return fu_fit_firmware_verify_crc32(firmware, img, img_hash, blob, error);
179
150
  if (g_strcmp0(algo, "md5") == 0) {
180
1
    return fu_fit_firmware_verify_checksum(firmware,
181
1
                   img,
182
1
                   img_hash,
183
1
                   G_CHECKSUM_MD5,
184
1
                   blob,
185
1
                   error);
186
1
  }
187
149
  if (g_strcmp0(algo, "sha1") == 0) {
188
2
    return fu_fit_firmware_verify_checksum(firmware,
189
2
                   img,
190
2
                   img_hash,
191
2
                   G_CHECKSUM_SHA1,
192
2
                   blob,
193
2
                   error);
194
2
  }
195
147
  if (g_strcmp0(algo, "sha256") == 0) {
196
6
    return fu_fit_firmware_verify_checksum(firmware,
197
6
                   img,
198
6
                   img_hash,
199
6
                   G_CHECKSUM_SHA256,
200
6
                   blob,
201
6
                   error);
202
6
  }
203
204
  /* ignore any hashes we do not support: success */
205
141
  return TRUE;
206
147
}
207
208
static gboolean
209
fu_fit_firmware_verify_image(FuFirmware *firmware,
210
           GInputStream *stream,
211
           FuFirmware *img,
212
           FuFirmwareParseFlags flags,
213
           GError **error)
214
342
{
215
342
  g_autoptr(GBytes) blob = NULL;
216
217
  /* sanity check */
218
342
  if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img), "type", NULL, error))
219
37
    return FALSE;
220
305
  if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img), "description", NULL, error))
221
8
    return FALSE;
222
223
  /* if has data */
224
297
  blob = fu_fdt_image_get_attr(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_DATA, NULL);
225
297
  if (blob == NULL) {
226
10
    guint32 data_size = 0x0;
227
10
    guint32 data_offset = 0x0;
228
229
    /* extra data outside of FIT image */
230
10
    if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img),
231
10
                 FU_FIT_FIRMWARE_ATTR_DATA_OFFSET,
232
10
                 &data_offset,
233
10
                 error))
234
9
      return FALSE;
235
1
    if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img),
236
1
                 FU_FIT_FIRMWARE_ATTR_DATA_SIZE,
237
1
                 &data_size,
238
1
                 error))
239
1
      return FALSE;
240
0
    blob = fu_input_stream_read_bytes(stream, data_offset, data_size, NULL, error);
241
0
    if (blob == NULL)
242
0
      return FALSE;
243
0
  }
244
287
  fu_dump_bytes(G_LOG_DOMAIN, "data", blob);
245
246
  /* verify any hashes we recognize */
247
287
  if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) {
248
234
    g_autoptr(GPtrArray) img_hashes = fu_firmware_get_images(img);
249
640
    for (guint i = 0; i < img_hashes->len; i++) {
250
536
      FuFirmware *img_hash = g_ptr_array_index(img_hashes, i);
251
536
      if (fu_firmware_get_id(img_hash) == NULL) {
252
31
        g_set_error_literal(error,
253
31
                FWUPD_ERROR,
254
31
                FWUPD_ERROR_INVALID_DATA,
255
31
                "no ID for image hash");
256
31
        return FALSE;
257
31
      }
258
505
      if (g_str_has_prefix(fu_firmware_get_id(img_hash), "hash")) {
259
295
        if (!fu_fit_firmware_verify_hash(firmware,
260
295
                 img,
261
295
                 img_hash,
262
295
                 blob,
263
295
                 error))
264
99
          return FALSE;
265
295
      }
266
505
    }
267
234
  }
268
269
  /* success */
270
157
  return TRUE;
271
287
}
272
273
static gboolean
274
fu_fit_firmware_verify_configuration(FuFirmware *firmware,
275
             FuFirmware *img,
276
             FuFirmwareParseFlags flags,
277
             GError **error)
278
47
{
279
  /* sanity check */
280
47
  if (!fu_fdt_image_get_attr_strlist(FU_FDT_IMAGE(img),
281
47
             FU_FIT_FIRMWARE_ATTR_COMPATIBLE,
282
47
             NULL,
283
47
             error))
284
29
    return FALSE;
285
286
  /* success */
287
18
  return TRUE;
288
47
}
289
290
static gboolean
291
fu_fit_firmware_parse(FuFirmware *firmware,
292
          GInputStream *stream,
293
          FuFirmwareParseFlags flags,
294
          GError **error)
295
2.32k
{
296
2.32k
  g_autoptr(FuFirmware) img_cfgs = NULL;
297
2.32k
  g_autoptr(FuFirmware) img_images = NULL;
298
2.32k
  g_autoptr(FuFirmware) img_root = NULL;
299
2.32k
  g_autoptr(GPtrArray) img_images_array = NULL;
300
2.32k
  g_autoptr(GPtrArray) img_cfgs_array = NULL;
301
302
  /* FuFdtFirmware->parse */
303
2.32k
  if (!FU_FIRMWARE_CLASS(fu_fit_firmware_parent_class)->parse(firmware, stream, flags, error))
304
1.06k
    return FALSE;
305
306
  /* sanity check */
307
1.26k
  img_root = fu_firmware_get_image_by_id(firmware, NULL, error);
308
1.26k
  if (img_root == NULL)
309
90
    return FALSE;
310
1.17k
  if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img_root),
311
1.17k
               FU_FIT_FIRMWARE_ATTR_TIMESTAMP,
312
1.17k
               NULL,
313
1.17k
               error))
314
229
    return FALSE;
315
316
  /* check the checksums of each image */
317
945
  img_images = fu_firmware_get_image_by_id(img_root, FU_FIT_FIRMWARE_ID_IMAGES, error);
318
945
  if (img_images == NULL)
319
41
    return FALSE;
320
904
  img_images_array = fu_firmware_get_images(img_images);
321
1.06k
  for (guint i = 0; i < img_images_array->len; i++) {
322
342
    FuFirmware *img = g_ptr_array_index(img_images_array, i);
323
342
    if (!fu_fit_firmware_verify_image(firmware, stream, img, flags, error))
324
185
      return FALSE;
325
342
  }
326
327
  /* check the setup of each configuration */
328
719
  img_cfgs = fu_firmware_get_image_by_id(img_root, FU_FIT_FIRMWARE_ID_CONFIGURATIONS, error);
329
719
  if (img_cfgs == NULL)
330
164
    return FALSE;
331
555
  img_cfgs_array = fu_firmware_get_images(img_cfgs);
332
573
  for (guint i = 0; i < img_cfgs_array->len; i++) {
333
47
    FuFirmware *img = g_ptr_array_index(img_cfgs_array, i);
334
47
    if (!fu_fit_firmware_verify_configuration(firmware, img, flags, error))
335
29
      return FALSE;
336
47
  }
337
338
  /* success */
339
526
  return TRUE;
340
555
}
341
342
static void
343
fu_fit_firmware_init(FuFitFirmware *self)
344
2.46k
{
345
2.46k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 1024);
346
2.46k
}
347
348
static void
349
fu_fit_firmware_class_init(FuFitFirmwareClass *klass)
350
1
{
351
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
352
1
  firmware_class->parse = fu_fit_firmware_parse;
353
1
}
354
355
/**
356
 * fu_fit_firmware_new:
357
 *
358
 * Creates a new #FuFirmware of sub type FIT
359
 *
360
 * Since: 1.8.2
361
 **/
362
FuFirmware *
363
fu_fit_firmware_new(void)
364
0
{
365
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_FIT_FIRMWARE, NULL));
366
0
}