/src/fwupd/libfwupdplugin/fu-coswid-firmware.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2022 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 232k | #define G_LOG_DOMAIN "FuFirmware" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-bytes.h" |
12 | | #include "fu-cbor-common.h" |
13 | | #include "fu-common.h" |
14 | | #include "fu-coswid-common.h" |
15 | | #include "fu-coswid-firmware.h" |
16 | | #include "fu-coswid-struct.h" |
17 | | #include "fu-input-stream.h" |
18 | | |
19 | | /** |
20 | | * FuCoswidFirmware: |
21 | | * |
22 | | * A coSWID SWID section. |
23 | | * |
24 | | * See also: [class@FuUswidFirmware] |
25 | | */ |
26 | | |
27 | | typedef struct { |
28 | | gchar *product; |
29 | | gchar *summary; |
30 | | gchar *colloquial_version; |
31 | | gchar *persistent_id; |
32 | | gchar *device_id; |
33 | | FuCoswidVersionScheme version_scheme; |
34 | | GPtrArray *links; /* of FuCoswidFirmwareLink */ |
35 | | GPtrArray *entities; /* of FuCoswidFirmwareEntity */ |
36 | | GPtrArray *payloads; /* of FuCoswidFirmwarePayload */ |
37 | | } FuCoswidFirmwarePrivate; |
38 | | |
39 | 449k | G_DEFINE_TYPE_WITH_PRIVATE(FuCoswidFirmware, fu_coswid_firmware, FU_TYPE_FIRMWARE) |
40 | 449k | #define GET_PRIVATE(o) (fu_coswid_firmware_get_instance_private(o)) |
41 | | |
42 | 110k | #define FU_COSWID_CBOR_MAX_DEPTH 10 |
43 | 110k | #define FU_COSWID_CBOR_MAX_ITEMS 100 |
44 | 110k | #define FU_COSWID_CBOR_MAX_LENGTH 10240 |
45 | | |
46 | | typedef struct { |
47 | | gchar *name; |
48 | | gchar *regid; |
49 | | guint8 roles; /* bitfield of FuCoswidEntityRole */ |
50 | | } FuCoswidFirmwareEntity; |
51 | | |
52 | | typedef struct { |
53 | | gchar *href; |
54 | | FuCoswidLinkRel rel; |
55 | | } FuCoswidFirmwareLink; |
56 | | |
57 | | typedef struct { |
58 | | GBytes *value; |
59 | | FuCoswidHashAlg alg_id; |
60 | | } FuCoswidFirmwareHash; |
61 | | |
62 | | typedef struct { |
63 | | gchar *name; |
64 | | guint64 size; |
65 | | GPtrArray *hashes; /* of FuCoswidFirmwareHash */ |
66 | | } FuCoswidFirmwarePayload; |
67 | | |
68 | | static void |
69 | | fu_coswid_firmware_entity_free(FuCoswidFirmwareEntity *entity) |
70 | 5.24k | { |
71 | 5.24k | g_free(entity->name); |
72 | 5.24k | g_free(entity->regid); |
73 | 5.24k | g_free(entity); |
74 | 5.24k | } |
75 | | |
76 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCoswidFirmwareEntity, fu_coswid_firmware_entity_free) |
77 | | |
78 | | static void |
79 | | fu_coswid_firmware_link_free(FuCoswidFirmwareLink *link) |
80 | 66.6k | { |
81 | 66.6k | g_free(link->href); |
82 | 66.6k | g_free(link); |
83 | 66.6k | } |
84 | | |
85 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCoswidFirmwareLink, fu_coswid_firmware_link_free) |
86 | | |
87 | | static void |
88 | | fu_coswid_firmware_hash_free(FuCoswidFirmwareHash *hash) |
89 | 906 | { |
90 | 906 | if (hash->value != NULL) |
91 | 689 | g_bytes_unref(hash->value); |
92 | 906 | g_free(hash); |
93 | 906 | } |
94 | | |
95 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCoswidFirmwareHash, fu_coswid_firmware_hash_free) |
96 | | |
97 | | static FuCoswidFirmwarePayload * |
98 | | fu_coswid_firmware_payload_new(void) |
99 | 4.04k | { |
100 | 4.04k | FuCoswidFirmwarePayload *payload = g_new0(FuCoswidFirmwarePayload, 1); |
101 | 4.04k | payload->hashes = |
102 | 4.04k | g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_hash_free); |
103 | 4.04k | return payload; |
104 | 4.04k | } |
105 | | |
106 | | static void |
107 | | fu_coswid_firmware_payload_free(FuCoswidFirmwarePayload *payload) |
108 | 4.04k | { |
109 | 4.04k | g_ptr_array_unref(payload->hashes); |
110 | 4.04k | g_free(payload->name); |
111 | 4.04k | g_free(payload); |
112 | 4.04k | } |
113 | | |
114 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCoswidFirmwarePayload, fu_coswid_firmware_payload_free) |
115 | | |
116 | | /* @userdata: a #FuCoswidFirmware */ |
117 | | static gboolean |
118 | | fu_coswid_firmware_parse_meta(FuCborItem *item, gpointer user_data, GError **error) |
119 | 2.91k | { |
120 | 2.91k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
121 | 2.91k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
122 | | |
123 | 15.0k | for (guint i = 0; i < fu_cbor_item_map_length(item); i++) { |
124 | 12.6k | FuCoswidTag tag_id = 0; |
125 | 12.6k | FuCborItem *item_key = NULL; |
126 | 12.6k | FuCborItem *item_value = NULL; |
127 | | |
128 | 12.6k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
129 | 12.6k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
130 | 246 | g_prefix_error(error, "failed to parse meta tag %u: ", (guint)i); |
131 | 246 | return FALSE; |
132 | 246 | } |
133 | 12.4k | if (tag_id == FU_COSWID_TAG_SUMMARY) { |
134 | 323 | g_free(priv->summary); |
135 | 323 | priv->summary = fu_coswid_read_string(item_value, error); |
136 | 323 | if (priv->summary == NULL) { |
137 | 106 | g_prefix_error_literal(error, "failed to parse summary: "); |
138 | 106 | return FALSE; |
139 | 106 | } |
140 | 12.1k | } else if (tag_id == FU_COSWID_TAG_COLLOQUIAL_VERSION) { |
141 | 349 | g_free(priv->colloquial_version); |
142 | 349 | priv->colloquial_version = fu_coswid_read_string(item_value, error); |
143 | 349 | if (priv->colloquial_version == NULL) { |
144 | 60 | g_prefix_error_literal(error, |
145 | 60 | "failed to parse colloquial-version: "); |
146 | 60 | return FALSE; |
147 | 60 | } |
148 | 11.7k | } else if (tag_id == FU_COSWID_TAG_PERSISTENT_ID) { |
149 | 270 | g_free(priv->persistent_id); |
150 | 270 | priv->persistent_id = fu_coswid_read_string(item_value, error); |
151 | 270 | if (priv->persistent_id == NULL) { |
152 | 105 | g_prefix_error_literal(error, "failed to parse persistent-id: "); |
153 | 105 | return FALSE; |
154 | 105 | } |
155 | 11.5k | } else { |
156 | 11.5k | g_debug("unhandled tag %s from %s", |
157 | 11.5k | fu_coswid_tag_to_string(tag_id), |
158 | 11.5k | fu_coswid_tag_to_string(FU_COSWID_TAG_SOFTWARE_META)); |
159 | 11.5k | } |
160 | 12.4k | } |
161 | | |
162 | | /* success */ |
163 | 2.40k | return TRUE; |
164 | 2.91k | } |
165 | | |
166 | | /* @userdata: a #FuCoswidFirmware */ |
167 | | static gboolean |
168 | | fu_coswid_firmware_parse_evidence(FuCborItem *item, gpointer user_data, GError **error) |
169 | 2.30k | { |
170 | 2.30k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
171 | 2.30k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
172 | | |
173 | 13.7k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
174 | 11.9k | FuCoswidTag tag_id = 0; |
175 | 11.9k | FuCborItem *item_key = NULL; |
176 | 11.9k | FuCborItem *item_value = NULL; |
177 | | |
178 | 11.9k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
179 | 11.9k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
180 | 441 | g_prefix_error(error, "failed to parse evidence tag %u: ", (guint)i); |
181 | 441 | return FALSE; |
182 | 441 | } |
183 | 11.4k | if (tag_id == FU_COSWID_TAG_DEVICE_ID) { |
184 | 212 | g_free(priv->device_id); |
185 | 212 | priv->device_id = fu_coswid_read_string(item_value, error); |
186 | 212 | if (priv->device_id == NULL) { |
187 | 56 | g_prefix_error_literal(error, "failed to parse device-id: "); |
188 | 56 | return FALSE; |
189 | 56 | } |
190 | 11.2k | } else { |
191 | 11.2k | g_debug("unhandled tag %s from %s", |
192 | 11.2k | fu_coswid_tag_to_string(tag_id), |
193 | 11.2k | fu_coswid_tag_to_string(FU_COSWID_TAG_SOFTWARE_META)); |
194 | 11.2k | } |
195 | 11.4k | } |
196 | | |
197 | | /* success */ |
198 | 1.80k | return TRUE; |
199 | 2.30k | } |
200 | | |
201 | | /* @userdata: a #FuCoswidFirmware */ |
202 | | static gboolean |
203 | | fu_coswid_firmware_parse_link(FuCborItem *item, gpointer user_data, GError **error) |
204 | 66.6k | { |
205 | 66.6k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
206 | 66.6k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
207 | 66.6k | g_autoptr(FuCoswidFirmwareLink) link = g_new0(FuCoswidFirmwareLink, 1); |
208 | | |
209 | 129k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
210 | 63.9k | FuCoswidTag tag_id = 0; |
211 | 63.9k | FuCborItem *item_key = NULL; |
212 | 63.9k | FuCborItem *item_value = NULL; |
213 | | |
214 | 63.9k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
215 | 63.9k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
216 | 489 | g_prefix_error(error, "failed to parse link tag %u: ", (guint)i); |
217 | 489 | return FALSE; |
218 | 489 | } |
219 | 63.4k | if (tag_id == FU_COSWID_TAG_HREF) { |
220 | 416 | g_free(link->href); |
221 | 416 | link->href = fu_coswid_read_string(item_value, error); |
222 | 416 | if (link->href == NULL) { |
223 | 106 | g_prefix_error_literal(error, "failed to parse link href: "); |
224 | 106 | return FALSE; |
225 | 106 | } |
226 | 62.9k | } else if (tag_id == FU_COSWID_TAG_REL) { |
227 | 11.8k | gint64 tmp = 0; |
228 | 11.8k | if (!fu_cbor_item_get_integer(item_value, &tmp, error)) { |
229 | 56 | g_prefix_error_literal(error, "failed to parse link rel: "); |
230 | 56 | return FALSE; |
231 | 56 | } |
232 | 11.8k | link->rel = tmp; |
233 | 51.1k | } else { |
234 | 51.1k | g_debug("unhandled tag %s from %s", |
235 | 51.1k | fu_coswid_tag_to_string(tag_id), |
236 | 51.1k | fu_coswid_tag_to_string(FU_COSWID_TAG_LINK)); |
237 | 51.1k | } |
238 | 63.4k | } |
239 | | |
240 | | /* success */ |
241 | 66.0k | g_ptr_array_add(priv->links, g_steal_pointer(&link)); |
242 | 66.0k | return TRUE; |
243 | 66.6k | } |
244 | | |
245 | | /* @userdata: a #FuCoswidFirmwarePayload */ |
246 | | static gboolean |
247 | | fu_coswid_firmware_parse_hash(FuCborItem *item, gpointer user_data, GError **error) |
248 | 906 | { |
249 | 906 | FuCoswidFirmwarePayload *payload = (FuCoswidFirmwarePayload *)user_data; |
250 | 906 | gint64 alg_id8 = 0; |
251 | 906 | g_autoptr(FuCoswidFirmwareHash) hash = g_new0(FuCoswidFirmwareHash, 1); |
252 | 906 | FuCborItem *hash_item_alg_id; |
253 | 906 | FuCborItem *hash_item_value; |
254 | | |
255 | | /* sanity check */ |
256 | 906 | if (fu_cbor_item_get_kind(item) != FU_CBOR_ITEM_KIND_ARRAY) { |
257 | 56 | g_set_error_literal(error, |
258 | 56 | FWUPD_ERROR, |
259 | 56 | FWUPD_ERROR_INVALID_DATA, |
260 | 56 | "hash item is not an array"); |
261 | 56 | return FALSE; |
262 | 56 | } |
263 | 850 | if (fu_cbor_item_array_length(item) != 2) { |
264 | 52 | g_set_error_literal(error, |
265 | 52 | FWUPD_ERROR, |
266 | 52 | FWUPD_ERROR_INVALID_DATA, |
267 | 52 | "hash array has invalid size"); |
268 | 52 | return FALSE; |
269 | 52 | } |
270 | 798 | hash_item_alg_id = fu_cbor_item_array_index(item, 0); |
271 | 798 | hash_item_value = fu_cbor_item_array_index(item, 1); |
272 | 798 | if (hash_item_alg_id == NULL || hash_item_value == NULL) { |
273 | 0 | g_set_error_literal(error, |
274 | 0 | FWUPD_ERROR, |
275 | 0 | FWUPD_ERROR_INVALID_DATA, |
276 | 0 | "invalid hash item"); |
277 | 0 | return FALSE; |
278 | 0 | } |
279 | 798 | if (!fu_cbor_item_get_integer(hash_item_alg_id, &alg_id8, error)) { |
280 | 44 | g_prefix_error_literal(error, "failed to parse hash alg-id: "); |
281 | 44 | return FALSE; |
282 | 44 | } |
283 | | |
284 | | /* success */ |
285 | 754 | hash->alg_id = alg_id8; |
286 | 754 | hash->value = fu_cbor_item_get_bytes(hash_item_value, error); |
287 | 754 | if (hash->value == NULL) { |
288 | 65 | g_prefix_error_literal(error, "failed to parse hash value: "); |
289 | 65 | return FALSE; |
290 | 65 | } |
291 | 689 | g_ptr_array_add(payload->hashes, g_steal_pointer(&hash)); |
292 | 689 | return TRUE; |
293 | 754 | } |
294 | | |
295 | | /* @userdata: a #FuCoswidFirmwarePayload */ |
296 | | static gboolean |
297 | | fu_coswid_firmware_parse_hash_array(FuCborItem *item, gpointer user_data, GError **error) |
298 | 327 | { |
299 | 705 | for (guint j = 0; j < fu_cbor_item_array_length(item); j++) { |
300 | 495 | FuCborItem *value = fu_cbor_item_array_index(item, j); |
301 | 495 | if (!fu_coswid_firmware_parse_hash(value, user_data, error)) |
302 | 117 | return FALSE; |
303 | 495 | } |
304 | 210 | return TRUE; |
305 | 327 | } |
306 | | |
307 | | /* @userdata: a #FuCoswidFirmware */ |
308 | | static gboolean |
309 | | fu_coswid_firmware_parse_file(FuCborItem *item, gpointer user_data, GError **error) |
310 | 4.04k | { |
311 | 4.04k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
312 | 4.04k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
313 | 4.04k | g_autoptr(FuCoswidFirmwarePayload) payload = fu_coswid_firmware_payload_new(); |
314 | | |
315 | 19.1k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
316 | 16.0k | FuCoswidTag tag_id = 0; |
317 | 16.0k | FuCborItem *item_key = NULL; |
318 | 16.0k | FuCborItem *item_value = NULL; |
319 | | |
320 | 16.0k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
321 | 16.0k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
322 | 87 | g_prefix_error(error, "failed to parse file tag %u: ", (guint)i); |
323 | 87 | return FALSE; |
324 | 87 | } |
325 | 16.0k | if (tag_id == FU_COSWID_TAG_FS_NAME) { |
326 | 221 | g_free(payload->name); |
327 | 221 | payload->name = fu_coswid_read_string(item_value, error); |
328 | 221 | if (payload->name == NULL) { |
329 | 64 | g_prefix_error_literal(error, "failed to parse payload name: "); |
330 | 64 | return FALSE; |
331 | 64 | } |
332 | 15.7k | } else if (tag_id == FU_COSWID_TAG_SIZE) { |
333 | 2.37k | gint64 tmp; |
334 | 2.37k | if (!fu_cbor_item_get_integer(item_value, &tmp, error)) |
335 | 59 | return FALSE; |
336 | 2.31k | if (tmp < 0) { |
337 | 465 | g_set_error_literal(error, |
338 | 465 | FWUPD_ERROR, |
339 | 465 | FWUPD_ERROR_INVALID_DATA, |
340 | 465 | "payload size cannot be negative"); |
341 | 465 | return FALSE; |
342 | 465 | } |
343 | 1.85k | payload->size = (guint64)tmp; |
344 | 13.4k | } else if (tag_id == FU_COSWID_TAG_HASH) { |
345 | 847 | if (fu_cbor_item_get_kind(item_value) == FU_CBOR_ITEM_KIND_ARRAY && |
346 | 781 | fu_cbor_item_array_length(item_value) >= 1) { |
347 | 738 | FuCborItem *value = fu_cbor_item_array_index(item_value, 0); |
348 | | /* we can't use fu_coswid_parse_one_or_many() here as |
349 | | * the hash is an array, not a map -- for some reason */ |
350 | 738 | if (fu_cbor_item_get_kind(value) == FU_CBOR_ITEM_KIND_ARRAY) { |
351 | 327 | if (!fu_coswid_firmware_parse_hash_array(item_value, |
352 | 327 | payload, |
353 | 327 | error)) |
354 | 117 | return FALSE; |
355 | 411 | } else { |
356 | 411 | if (!fu_coswid_firmware_parse_hash(item_value, |
357 | 411 | payload, |
358 | 411 | error)) |
359 | 100 | return FALSE; |
360 | 411 | } |
361 | 738 | } else { |
362 | 109 | g_set_error_literal(error, |
363 | 109 | FWUPD_ERROR, |
364 | 109 | FWUPD_ERROR_INVALID_DATA, |
365 | 109 | "hashes neither an array or array of array"); |
366 | 109 | return FALSE; |
367 | 109 | } |
368 | 12.5k | } else { |
369 | 12.5k | g_debug("unhandled tag %s from %s", |
370 | 12.5k | fu_coswid_tag_to_string(tag_id), |
371 | 12.5k | fu_coswid_tag_to_string(FU_COSWID_TAG_FILE)); |
372 | 12.5k | } |
373 | 16.0k | } |
374 | | |
375 | | /* success */ |
376 | 3.04k | g_ptr_array_add(priv->payloads, g_steal_pointer(&payload)); |
377 | 3.04k | return TRUE; |
378 | 4.04k | } |
379 | | |
380 | | /* @userdata: a #FuCoswidFirmware */ |
381 | | static gboolean |
382 | | fu_coswid_firmware_parse_path_elements(FuCborItem *item, gpointer user_data, GError **error) |
383 | 1.65k | { |
384 | 1.65k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
385 | 10.1k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
386 | 8.65k | FuCoswidTag tag_id = 0; |
387 | 8.65k | FuCborItem *item_key = NULL; |
388 | 8.65k | FuCborItem *item_value = NULL; |
389 | | |
390 | 8.65k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
391 | 8.65k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
392 | 82 | g_prefix_error(error, "failed to parse elements tag %u: ", (guint)i); |
393 | 82 | return FALSE; |
394 | 82 | } |
395 | 8.57k | if (tag_id == FU_COSWID_TAG_FILE) { |
396 | 774 | if (!fu_coswid_parse_one_or_many(item_value, |
397 | 774 | fu_coswid_firmware_parse_file, |
398 | 774 | self, /* user_data */ |
399 | 774 | error)) |
400 | 71 | return FALSE; |
401 | 7.80k | } else { |
402 | 7.80k | g_debug("unhandled tag %s from %s", |
403 | 7.80k | fu_coswid_tag_to_string(tag_id), |
404 | 7.80k | fu_coswid_tag_to_string(FU_COSWID_TAG_PATH_ELEMENTS)); |
405 | 7.80k | } |
406 | 8.57k | } |
407 | | |
408 | | /* success */ |
409 | 1.50k | return TRUE; |
410 | 1.65k | } |
411 | | |
412 | | /* @userdata: a #FuCoswidFirmware */ |
413 | | static gboolean |
414 | | fu_coswid_firmware_parse_directory(FuCborItem *item, gpointer user_data, GError **error) |
415 | 1.79k | { |
416 | 1.79k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
417 | 11.3k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
418 | 9.78k | FuCoswidTag tag_id = 0; |
419 | 9.78k | FuCborItem *item_key = NULL; |
420 | 9.78k | FuCborItem *item_value = NULL; |
421 | | |
422 | 9.78k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
423 | 9.78k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
424 | 95 | g_prefix_error(error, "failed to parse directory tag %u: ", (guint)i); |
425 | 95 | return FALSE; |
426 | 95 | } |
427 | 9.69k | if (tag_id == FU_COSWID_TAG_PATH_ELEMENTS) { |
428 | 986 | if (!fu_coswid_parse_one_or_many(item_value, |
429 | 986 | fu_coswid_firmware_parse_path_elements, |
430 | 986 | self, /* user_data */ |
431 | 986 | error)) |
432 | 177 | return FALSE; |
433 | 8.70k | } else { |
434 | 8.70k | g_debug("unhandled tag %s from %s", |
435 | 8.70k | fu_coswid_tag_to_string(tag_id), |
436 | 8.70k | fu_coswid_tag_to_string(FU_COSWID_TAG_DIRECTORY)); |
437 | 8.70k | } |
438 | 9.69k | } |
439 | | |
440 | | /* success */ |
441 | 1.52k | return TRUE; |
442 | 1.79k | } |
443 | | |
444 | | /* @userdata: a #FuCoswidFirmware */ |
445 | | static gboolean |
446 | | fu_coswid_firmware_parse_payload(FuCborItem *item, gpointer user_data, GError **error) |
447 | 5.18k | { |
448 | 5.18k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
449 | 26.7k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
450 | 23.4k | FuCoswidTag tag_id = 0; |
451 | 23.4k | FuCborItem *item_key = NULL; |
452 | 23.4k | FuCborItem *item_value = NULL; |
453 | | |
454 | 23.4k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
455 | 23.4k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
456 | 247 | g_prefix_error(error, "failed to parse payload tag %u: ", (guint)i); |
457 | 247 | return FALSE; |
458 | 247 | } |
459 | 23.2k | if (tag_id == FU_COSWID_TAG_FILE) { |
460 | 2.59k | if (!fu_coswid_parse_one_or_many(item_value, |
461 | 2.59k | fu_coswid_firmware_parse_file, |
462 | 2.59k | self, /* user_data */ |
463 | 2.59k | error)) |
464 | 1.14k | return FALSE; |
465 | 20.6k | } else if (tag_id == FU_COSWID_TAG_DIRECTORY) { |
466 | 1.58k | if (!fu_coswid_parse_one_or_many(item_value, |
467 | 1.58k | fu_coswid_firmware_parse_directory, |
468 | 1.58k | self, /* user_data */ |
469 | 1.58k | error)) |
470 | 454 | return FALSE; |
471 | 19.0k | } else { |
472 | 19.0k | g_debug("unhandled tag %s from %s", |
473 | 19.0k | fu_coswid_tag_to_string(tag_id), |
474 | 19.0k | fu_coswid_tag_to_string(FU_COSWID_TAG_PAYLOAD)); |
475 | 19.0k | } |
476 | 23.2k | } |
477 | | |
478 | | /* success */ |
479 | 3.33k | return TRUE; |
480 | 5.18k | } |
481 | | |
482 | | static gboolean |
483 | | fu_coswid_firmware_parse_entity_name(FuCoswidFirmwareEntity *entity, |
484 | | FuCborItem *item, |
485 | | GError **error) |
486 | 6.40k | { |
487 | | /* we might be calling this twice... */ |
488 | 6.40k | g_free(entity->name); |
489 | | |
490 | 6.40k | entity->name = fu_coswid_read_string(item, error); |
491 | 6.40k | if (entity->name == NULL) { |
492 | 56 | g_prefix_error_literal(error, "failed to parse entity name: "); |
493 | 56 | return FALSE; |
494 | 56 | } |
495 | | |
496 | | /* success */ |
497 | 6.35k | return TRUE; |
498 | 6.40k | } |
499 | | |
500 | | static gboolean |
501 | | fu_coswid_firmware_parse_entity_regid(FuCoswidFirmwareEntity *entity, |
502 | | FuCborItem *item, |
503 | | GError **error) |
504 | 482 | { |
505 | | /* we might be calling this twice... */ |
506 | 482 | g_free(entity->regid); |
507 | | |
508 | 482 | entity->regid = fu_coswid_read_string(item, error); |
509 | 482 | if (entity->regid == NULL) { |
510 | 103 | g_prefix_error_literal(error, "failed to parse entity regid: "); |
511 | 103 | return FALSE; |
512 | 103 | } |
513 | | |
514 | | /* success */ |
515 | 379 | return TRUE; |
516 | 482 | } |
517 | | |
518 | | static gboolean |
519 | | fu_coswid_firmware_parse_entity_role(FuCoswidFirmwareEntity *entity, |
520 | | FuCborItem *item, |
521 | | GError **error) |
522 | 3.91k | { |
523 | 3.91k | if (fu_cbor_item_get_kind(item) == FU_CBOR_ITEM_KIND_INTEGER) { |
524 | 2.70k | gint64 tmp = 0; |
525 | 2.70k | if (!fu_cbor_item_get_integer(item, &tmp, error)) { |
526 | 0 | g_prefix_error_literal(error, "failed to parse entity role: "); |
527 | 0 | return FALSE; |
528 | 0 | } |
529 | 2.70k | if (tmp <= FU_COSWID_ENTITY_ROLE_UNKNOWN || tmp >= FU_COSWID_ENTITY_ROLE_LAST) { |
530 | 637 | g_set_error(error, |
531 | 637 | FWUPD_ERROR, |
532 | 637 | FWUPD_ERROR_INVALID_DATA, |
533 | 637 | "invalid entity role 0x%x", |
534 | 637 | (guint)tmp); |
535 | 637 | return FALSE; |
536 | 637 | } |
537 | 2.06k | FU_BIT_SET(entity->roles, tmp); |
538 | | |
539 | 2.06k | } else if (fu_cbor_item_get_kind(item) == FU_CBOR_ITEM_KIND_ARRAY) { |
540 | 2.48k | for (guint j = 0; j < fu_cbor_item_array_length(item); j++) { |
541 | 2.20k | gint64 tmp = 0; |
542 | 2.20k | FuCborItem *value = fu_cbor_item_array_index(item, j); |
543 | 2.20k | if (!fu_cbor_item_get_integer(value, &tmp, error)) { |
544 | 74 | g_prefix_error_literal(error, "failed to parse entity role: "); |
545 | 74 | return FALSE; |
546 | 74 | } |
547 | 2.13k | if (tmp <= FU_COSWID_ENTITY_ROLE_UNKNOWN || |
548 | 1.71k | tmp >= FU_COSWID_ENTITY_ROLE_LAST) { |
549 | 792 | g_set_error(error, |
550 | 792 | FWUPD_ERROR, |
551 | 792 | FWUPD_ERROR_INVALID_DATA, |
552 | 792 | "invalid entity role 0x%x", |
553 | 792 | (guint)tmp); |
554 | 792 | return FALSE; |
555 | 792 | } |
556 | 1.33k | FU_BIT_SET(entity->roles, tmp); |
557 | 1.33k | } |
558 | 1.14k | } else { |
559 | 61 | g_set_error_literal(error, |
560 | 61 | FWUPD_ERROR, |
561 | 61 | FWUPD_ERROR_INVALID_DATA, |
562 | 61 | "entity role item is not an uint or array"); |
563 | 61 | return FALSE; |
564 | 61 | } |
565 | | |
566 | | /* success */ |
567 | 2.34k | return TRUE; |
568 | 3.91k | } |
569 | | |
570 | | /* @userdata: a #FuCoswidFirmware */ |
571 | | static gboolean |
572 | | fu_coswid_firmware_parse_entity(FuCborItem *item, gpointer user_data, GError **error) |
573 | 5.24k | { |
574 | 5.24k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); |
575 | 5.24k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
576 | 5.24k | g_autoptr(FuCoswidFirmwareEntity) entity = g_new0(FuCoswidFirmwareEntity, 1); |
577 | | |
578 | 57.0k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
579 | 53.9k | FuCoswidTag tag_id = 0; |
580 | 53.9k | FuCborItem *item_key = NULL; |
581 | 53.9k | FuCborItem *item_value = NULL; |
582 | | |
583 | 53.9k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
584 | 53.9k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
585 | 412 | g_prefix_error(error, "failed to parse entity tag %u: ", (guint)i); |
586 | 412 | return FALSE; |
587 | 412 | } |
588 | 53.5k | if (tag_id == FU_COSWID_TAG_ENTITY_NAME) { |
589 | 6.40k | if (!fu_coswid_firmware_parse_entity_name(entity, item_value, error)) |
590 | 56 | return FALSE; |
591 | 47.1k | } else if (tag_id == FU_COSWID_TAG_REG_ID) { |
592 | 482 | if (!fu_coswid_firmware_parse_entity_regid(entity, item_value, error)) |
593 | 103 | return FALSE; |
594 | 46.6k | } else if (tag_id == FU_COSWID_TAG_ROLE) { |
595 | 3.91k | if (!fu_coswid_firmware_parse_entity_role(entity, item_value, error)) |
596 | 1.56k | return FALSE; |
597 | 42.7k | } else { |
598 | 42.7k | g_debug("unhandled tag %s from %s", |
599 | 42.7k | fu_coswid_tag_to_string(tag_id), |
600 | 42.7k | fu_coswid_tag_to_string(FU_COSWID_TAG_ENTITY)); |
601 | 42.7k | } |
602 | 53.5k | } |
603 | | |
604 | | /* sanity check */ |
605 | 3.10k | if (entity->name == NULL) { |
606 | 1.22k | g_set_error_literal(error, |
607 | 1.22k | FWUPD_ERROR, |
608 | 1.22k | FWUPD_ERROR_INVALID_DATA, |
609 | 1.22k | "entity does not have a name"); |
610 | 1.22k | return FALSE; |
611 | 1.22k | } |
612 | 1.88k | if (entity->roles == 0) { |
613 | 81 | g_set_error_literal(error, |
614 | 81 | FWUPD_ERROR, |
615 | 81 | FWUPD_ERROR_INVALID_DATA, |
616 | 81 | "entity has no roles"); |
617 | 81 | return FALSE; |
618 | 81 | } |
619 | | |
620 | | /* success */ |
621 | 1.79k | g_ptr_array_add(priv->entities, g_steal_pointer(&entity)); |
622 | 1.79k | return TRUE; |
623 | 1.88k | } |
624 | | |
625 | | static gboolean |
626 | | fu_coswid_firmware_parse(FuFirmware *firmware, |
627 | | GInputStream *stream, |
628 | | FuFirmwareParseFlags flags, |
629 | | GError **error) |
630 | 110k | { |
631 | 110k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); |
632 | 110k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
633 | 110k | gsize offset = 0; |
634 | 110k | g_autoptr(FuCborItem) item = NULL; |
635 | | |
636 | 110k | item = fu_cbor_parse(stream, |
637 | 110k | &offset, |
638 | 110k | FU_COSWID_CBOR_MAX_DEPTH, |
639 | 110k | FU_COSWID_CBOR_MAX_ITEMS, |
640 | 110k | FU_COSWID_CBOR_MAX_LENGTH, |
641 | 110k | error); |
642 | 110k | if (item == NULL) |
643 | 26.6k | return FALSE; |
644 | 84.1k | fu_firmware_set_size(firmware, offset); |
645 | | |
646 | | /* pretty-print the result */ |
647 | 84.1k | if (g_getenv("FWUPD_CBOR_VERBOSE") != NULL) { |
648 | 0 | g_autofree gchar *str = fu_cbor_item_to_string(item); |
649 | 0 | g_debug("%s", str); |
650 | 0 | } |
651 | | |
652 | | /* sanity check */ |
653 | 84.1k | if (fu_cbor_item_get_kind(item) != FU_CBOR_ITEM_KIND_MAP) { |
654 | 1.04k | g_set_error_literal(error, |
655 | 1.04k | FWUPD_ERROR, |
656 | 1.04k | FWUPD_ERROR_INVALID_DATA, |
657 | 1.04k | "root item is not a map"); |
658 | 1.04k | return FALSE; |
659 | 1.04k | } |
660 | | |
661 | | /* parse out anything interesting */ |
662 | 232k | for (gsize i = 0; i < fu_cbor_item_map_length(item); i++) { |
663 | 163k | FuCoswidTag tag_id = 0; |
664 | 163k | FuCborItem *item_key = NULL; |
665 | 163k | FuCborItem *item_value = NULL; |
666 | | |
667 | 163k | fu_cbor_item_map_index(item, i, &item_key, &item_value); |
668 | 163k | if (!fu_coswid_read_tag(item_key, &tag_id, error)) { |
669 | 1.88k | g_prefix_error(error, "failed to parse root tag %u: ", (guint)i); |
670 | 1.88k | return FALSE; |
671 | 1.88k | } |
672 | | |
673 | | /* identity can be specified as a string or in binary */ |
674 | 161k | if (tag_id == FU_COSWID_TAG_TAG_ID) { |
675 | 8.26k | g_autofree gchar *str = fu_coswid_read_string(item_value, error); |
676 | 8.26k | if (str == NULL) { |
677 | 2.49k | g_prefix_error_literal(error, "failed to parse tag-id: "); |
678 | 2.49k | return FALSE; |
679 | 2.49k | } |
680 | 5.77k | fu_firmware_set_id(firmware, str); |
681 | 153k | } else if (tag_id == FU_COSWID_TAG_SOFTWARE_NAME) { |
682 | 3.44k | g_free(priv->product); |
683 | 3.44k | priv->product = fu_coswid_read_string(item_value, error); |
684 | 3.44k | if (priv->product == NULL) { |
685 | 658 | g_prefix_error_literal(error, "failed to parse product: "); |
686 | 658 | return FALSE; |
687 | 658 | } |
688 | 149k | } else if (tag_id == FU_COSWID_TAG_SOFTWARE_VERSION) { |
689 | 2.57k | g_autofree gchar *str = fu_coswid_read_string(item_value, error); |
690 | 2.57k | if (str == NULL) { |
691 | 407 | g_prefix_error_literal(error, "failed to parse software-version: "); |
692 | 407 | return FALSE; |
693 | 407 | } |
694 | 2.16k | fu_firmware_set_version(firmware, str); |
695 | 147k | } else if (tag_id == FU_COSWID_TAG_VERSION_SCHEME) { |
696 | 3.11k | if (!fu_coswid_read_version_scheme(item_value, |
697 | 3.11k | &priv->version_scheme, |
698 | 3.11k | error)) |
699 | 288 | return FALSE; |
700 | 144k | } else if (tag_id == FU_COSWID_TAG_SOFTWARE_META) { |
701 | 3.93k | if (!fu_coswid_parse_one_or_many(item_value, |
702 | 3.93k | fu_coswid_firmware_parse_meta, |
703 | 3.93k | self, /* user_data */ |
704 | 3.93k | error)) |
705 | 1.01k | return FALSE; |
706 | 140k | } else if (tag_id == FU_COSWID_TAG_EVIDENCE) { |
707 | 5.06k | if (!fu_coswid_parse_one_or_many(item_value, |
708 | 5.06k | fu_coswid_firmware_parse_evidence, |
709 | 5.06k | self, /* user_data */ |
710 | 5.06k | error)) |
711 | 797 | return FALSE; |
712 | 135k | } else if (tag_id == FU_COSWID_TAG_LINK) { |
713 | 56.3k | if (!fu_coswid_parse_one_or_many(item_value, |
714 | 56.3k | fu_coswid_firmware_parse_link, |
715 | 56.3k | self, /* user_data */ |
716 | 56.3k | error)) |
717 | 907 | return FALSE; |
718 | 78.7k | } else if (tag_id == FU_COSWID_TAG_PAYLOAD) { |
719 | 4.84k | if (!fu_coswid_parse_one_or_many(item_value, |
720 | 4.84k | fu_coswid_firmware_parse_payload, |
721 | 4.84k | self, /* user_data */ |
722 | 4.84k | error)) |
723 | 2.20k | return FALSE; |
724 | 73.9k | } else if (tag_id == FU_COSWID_TAG_ENTITY) { |
725 | 5.95k | if (!fu_coswid_parse_one_or_many(item_value, |
726 | 5.95k | fu_coswid_firmware_parse_entity, |
727 | 5.95k | self, /* user_data */ |
728 | 5.95k | error)) |
729 | 3.76k | return FALSE; |
730 | 67.9k | } else { |
731 | 67.9k | g_debug("unhandled tag %s from root", fu_coswid_tag_to_string(tag_id)); |
732 | 67.9k | } |
733 | 161k | } |
734 | | |
735 | | /* device not supported */ |
736 | 68.7k | if (fu_firmware_get_id(firmware) == NULL && fu_firmware_get_version(firmware) == NULL && |
737 | 63.0k | priv->product == NULL && priv->entities->len == 0 && priv->links->len == 0) { |
738 | 5.32k | g_set_error_literal(error, |
739 | 5.32k | FWUPD_ERROR, |
740 | 5.32k | FWUPD_ERROR_NOT_SUPPORTED, |
741 | 5.32k | "not enough SBOM data"); |
742 | 5.32k | return FALSE; |
743 | 5.32k | } |
744 | | |
745 | | /* success */ |
746 | 63.4k | return TRUE; |
747 | 68.7k | } |
748 | | |
749 | | static gchar * |
750 | | fu_coswid_firmware_get_checksum(FuFirmware *firmware, GChecksumType csum_kind, GError **error) |
751 | 0 | { |
752 | 0 | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); |
753 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
754 | 0 | FuCoswidHashAlg alg_id = FU_COSWID_HASH_ALG_UNKNOWN; |
755 | 0 | struct { |
756 | 0 | GChecksumType kind; |
757 | 0 | FuCoswidHashAlg alg_id; |
758 | 0 | } csum_kinds[] = {{G_CHECKSUM_SHA256, FU_COSWID_HASH_ALG_SHA256}, |
759 | 0 | {G_CHECKSUM_SHA384, FU_COSWID_HASH_ALG_SHA384}, |
760 | 0 | {G_CHECKSUM_SHA512, FU_COSWID_HASH_ALG_SHA512}, |
761 | 0 | {0, FU_COSWID_HASH_ALG_UNKNOWN}}; |
762 | | |
763 | | /* convert to FuCoswidHashAlg */ |
764 | 0 | for (guint i = 0; csum_kinds[i].kind != 0; i++) { |
765 | 0 | if (csum_kinds[i].kind == csum_kind) { |
766 | 0 | alg_id = csum_kinds[i].alg_id; |
767 | 0 | break; |
768 | 0 | } |
769 | 0 | } |
770 | 0 | if (alg_id == FU_COSWID_HASH_ALG_UNKNOWN) { |
771 | 0 | g_set_error(error, |
772 | 0 | FWUPD_ERROR, |
773 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
774 | 0 | "cannot convert %s", |
775 | 0 | fwupd_checksum_type_to_string_display(csum_kind)); |
776 | 0 | return NULL; |
777 | 0 | } |
778 | | |
779 | | /* find the correct hash kind */ |
780 | 0 | for (guint i = 0; i < priv->payloads->len; i++) { |
781 | 0 | FuCoswidFirmwarePayload *payload = g_ptr_array_index(priv->payloads, i); |
782 | 0 | for (guint j = 0; j < payload->hashes->len; j++) { |
783 | 0 | FuCoswidFirmwareHash *hash = g_ptr_array_index(payload->hashes, j); |
784 | 0 | if (hash->alg_id == alg_id) |
785 | 0 | return fu_bytes_to_string(hash->value); |
786 | 0 | } |
787 | 0 | } |
788 | 0 | g_set_error(error, |
789 | 0 | FWUPD_ERROR, |
790 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
791 | 0 | "no hash kind %s", |
792 | 0 | fwupd_checksum_type_to_string_display(csum_kind)); |
793 | 0 | return NULL; |
794 | 0 | } |
795 | | |
796 | | /** |
797 | | * fu_coswid_firmware_get_product: |
798 | | * @self: a #FuCoswidFirmware |
799 | | * |
800 | | * Gets the product name. |
801 | | * |
802 | | * Returns: string, or %NULL for unset |
803 | | * |
804 | | * Since: 2.0.12 |
805 | | **/ |
806 | | const gchar * |
807 | | fu_coswid_firmware_get_product(FuCoswidFirmware *self) |
808 | 0 | { |
809 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
810 | 0 | g_return_val_if_fail(FU_IS_COSWID_FIRMWARE(self), NULL); |
811 | 0 | return priv->product; |
812 | 0 | } |
813 | | |
814 | | /** |
815 | | * fu_coswid_firmware_get_persistent_id: |
816 | | * @self: a #FuCoswidFirmware |
817 | | * |
818 | | * Gets the persistent ID. |
819 | | * |
820 | | * Returns: string, or %NULL for unset |
821 | | * |
822 | | * Since: 2.0.17 |
823 | | **/ |
824 | | const gchar * |
825 | | fu_coswid_firmware_get_persistent_id(FuCoswidFirmware *self) |
826 | 0 | { |
827 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
828 | 0 | g_return_val_if_fail(FU_IS_COSWID_FIRMWARE(self), NULL); |
829 | 0 | return priv->persistent_id; |
830 | 0 | } |
831 | | |
832 | | /** |
833 | | * fu_coswid_firmware_get_device_id: |
834 | | * @self: a #FuCoswidFirmware |
835 | | * |
836 | | * Gets the device ID. |
837 | | * |
838 | | * Returns: string, or %NULL for unset |
839 | | * |
840 | | * Since: 2.0.17 |
841 | | **/ |
842 | | const gchar * |
843 | | fu_coswid_firmware_get_device_id(FuCoswidFirmware *self) |
844 | 0 | { |
845 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
846 | 0 | g_return_val_if_fail(FU_IS_COSWID_FIRMWARE(self), NULL); |
847 | 0 | return priv->device_id; |
848 | 0 | } |
849 | | |
850 | | static gboolean |
851 | | fu_coswid_firmware_write_hash(FuCborItem *root, FuCoswidFirmwareHash *hash, GError **error) |
852 | 270 | { |
853 | 270 | g_autoptr(FuCborItem) item_hash = fu_cbor_item_new_array(); |
854 | 270 | g_autoptr(FuCborItem) item_hash_alg_id = fu_cbor_item_new_integer(hash->alg_id); |
855 | 270 | g_autoptr(FuCborItem) item_hash_value = fu_cbor_item_new_bytes(hash->value); |
856 | 270 | if (!fu_cbor_item_array_append(item_hash, item_hash_alg_id, error)) |
857 | 0 | return FALSE; |
858 | 270 | if (!fu_cbor_item_array_append(item_hash, item_hash_value, error)) |
859 | 0 | return FALSE; |
860 | 270 | if (!fu_cbor_item_array_append(root, item_hash, error)) |
861 | 0 | return FALSE; |
862 | | |
863 | | /* success */ |
864 | 270 | return TRUE; |
865 | 270 | } |
866 | | |
867 | | static gboolean |
868 | | fu_coswid_firmware_write_payload(FuCborItem *root, FuCoswidFirmwarePayload *payload, GError **error) |
869 | 753 | { |
870 | 753 | g_autoptr(FuCborItem) item_payload = fu_cbor_item_new_map(); |
871 | 753 | g_autoptr(FuCborItem) item_file = fu_cbor_item_new_map(); |
872 | 753 | if (payload->name != NULL) |
873 | 21 | fu_coswid_write_tag_string(item_file, FU_COSWID_TAG_FS_NAME, payload->name); |
874 | 753 | if (payload->size != 0) |
875 | 205 | fu_coswid_write_tag_integer(item_file, FU_COSWID_TAG_SIZE, payload->size); |
876 | 753 | if (payload->hashes->len > 0) { |
877 | 100 | g_autoptr(FuCborItem) item_hashes = fu_cbor_item_new_array(); |
878 | 370 | for (guint j = 0; j < payload->hashes->len; j++) { |
879 | 270 | FuCoswidFirmwareHash *hash = g_ptr_array_index(payload->hashes, j); |
880 | 270 | if (!fu_coswid_firmware_write_hash(item_hashes, hash, error)) { |
881 | 0 | g_prefix_error_literal(error, "failed to add payload: "); |
882 | 0 | return FALSE; |
883 | 0 | } |
884 | 270 | } |
885 | 100 | fu_coswid_write_tag_item(item_file, FU_COSWID_TAG_HASH, item_hashes); |
886 | 100 | } |
887 | 753 | fu_coswid_write_tag_item(item_payload, FU_COSWID_TAG_FILE, item_file); |
888 | 753 | return fu_cbor_item_array_append(root, item_payload, error); |
889 | 753 | } |
890 | | |
891 | | static gboolean |
892 | | fu_coswid_firmware_write_entity(FuCborItem *root, FuCoswidFirmwareEntity *entity, GError **error) |
893 | 846 | { |
894 | 846 | g_autoptr(FuCborItem) item_entity = fu_cbor_item_new_map(); |
895 | 846 | g_autoptr(FuCborItem) item_roles = fu_cbor_item_new_array(); |
896 | 846 | if (entity->name != NULL) |
897 | 846 | fu_coswid_write_tag_string(item_entity, FU_COSWID_TAG_ENTITY_NAME, entity->name); |
898 | 846 | if (entity->regid != NULL) |
899 | 83 | fu_coswid_write_tag_string(item_entity, FU_COSWID_TAG_REG_ID, entity->regid); |
900 | 6.76k | for (guint j = 0; j < FU_COSWID_ENTITY_ROLE_LAST; j++) { |
901 | 5.92k | if (FU_BIT_IS_SET(entity->roles, j)) { |
902 | 892 | g_autoptr(FuCborItem) item_role = fu_cbor_item_new_integer(j); |
903 | 892 | if (!fu_cbor_item_array_append(item_roles, item_role, error)) |
904 | 0 | return FALSE; |
905 | 892 | } |
906 | 5.92k | } |
907 | 846 | fu_coswid_write_tag_item(item_entity, FU_COSWID_TAG_ROLE, item_roles); |
908 | 846 | return fu_cbor_item_array_append(root, item_entity, error); |
909 | 846 | } |
910 | | |
911 | | static gboolean |
912 | | fu_coswid_firmware_write_evidence(FuCborItem *root, const gchar *device_id, GError **error) |
913 | 1 | { |
914 | 1 | g_autoptr(FuCborItem) item_entity = fu_cbor_item_new_map(); |
915 | 1 | fu_coswid_write_tag_string(item_entity, FU_COSWID_TAG_DEVICE_ID, device_id); |
916 | 1 | return fu_cbor_item_array_append(root, item_entity, error); |
917 | 1 | } |
918 | | |
919 | | static GByteArray * |
920 | | fu_coswid_firmware_write(FuFirmware *firmware, GError **error) |
921 | 20.5k | { |
922 | 20.5k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); |
923 | 20.5k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
924 | 20.5k | g_autoptr(FuCborItem) root = fu_cbor_item_new_map(); |
925 | 20.5k | g_autoptr(FuCborItem) item_meta = fu_cbor_item_new_map(); |
926 | | |
927 | | /* preallocate the map structure */ |
928 | 20.5k | fu_coswid_write_tag_string(root, FU_COSWID_TAG_LANG, "en-US"); |
929 | 20.5k | if (fu_firmware_get_id(firmware) != NULL) { |
930 | 3.01k | fwupd_guid_t uuid = {0}; |
931 | 3.01k | if (fwupd_guid_from_string(fu_firmware_get_id(firmware), |
932 | 3.01k | &uuid, |
933 | 3.01k | FWUPD_GUID_FLAG_NONE, |
934 | 3.01k | NULL)) { |
935 | 1.28k | fu_coswid_write_tag_bytestring(root, |
936 | 1.28k | FU_COSWID_TAG_TAG_ID, |
937 | 1.28k | (const guint8 *)&uuid, |
938 | 1.28k | sizeof(uuid)); |
939 | 1.73k | } else { |
940 | 1.73k | fu_coswid_write_tag_string(root, |
941 | 1.73k | FU_COSWID_TAG_TAG_ID, |
942 | 1.73k | fu_firmware_get_id(firmware)); |
943 | 1.73k | } |
944 | 3.01k | } |
945 | 20.5k | fu_coswid_write_tag_bool(root, FU_COSWID_TAG_CORPUS, TRUE); |
946 | 20.5k | if (priv->product != NULL) |
947 | 617 | fu_coswid_write_tag_string(root, FU_COSWID_TAG_SOFTWARE_NAME, priv->product); |
948 | 20.5k | if (fu_firmware_get_version(firmware) != NULL) { |
949 | 529 | fu_coswid_write_tag_string(root, |
950 | 529 | FU_COSWID_TAG_SOFTWARE_VERSION, |
951 | 529 | fu_firmware_get_version(firmware)); |
952 | 529 | } |
953 | 20.5k | if (priv->version_scheme != FU_COSWID_VERSION_SCHEME_UNKNOWN) |
954 | 20.5k | fu_coswid_write_tag_integer(root, |
955 | 20.5k | FU_COSWID_TAG_VERSION_SCHEME, |
956 | 20.5k | priv->version_scheme); |
957 | 20.5k | fu_coswid_write_tag_item(root, FU_COSWID_TAG_SOFTWARE_META, item_meta); |
958 | 20.5k | fu_coswid_write_tag_string(item_meta, FU_COSWID_TAG_GENERATOR, PACKAGE_NAME); |
959 | 20.5k | if (priv->summary != NULL) |
960 | 26 | fu_coswid_write_tag_string(item_meta, FU_COSWID_TAG_SUMMARY, priv->summary); |
961 | 20.5k | if (priv->colloquial_version != NULL) { |
962 | 29 | fu_coswid_write_tag_string(item_meta, |
963 | 29 | FU_COSWID_TAG_COLLOQUIAL_VERSION, |
964 | 29 | priv->colloquial_version); |
965 | 29 | } |
966 | 20.5k | if (priv->persistent_id != NULL) { |
967 | 22 | fu_coswid_write_tag_string(item_meta, |
968 | 22 | FU_COSWID_TAG_PERSISTENT_ID, |
969 | 22 | priv->persistent_id); |
970 | 22 | } |
971 | | |
972 | | /* add evidence */ |
973 | 20.5k | if (priv->device_id != NULL) { |
974 | 1 | g_autoptr(FuCborItem) items = fu_cbor_item_new_array(); |
975 | 1 | if (!fu_coswid_firmware_write_evidence(items, priv->device_id, error)) |
976 | 0 | return NULL; |
977 | 1 | fu_coswid_write_tag_item(root, FU_COSWID_TAG_EVIDENCE, items); |
978 | 1 | } |
979 | | |
980 | | /* add entities */ |
981 | 20.5k | if (priv->entities->len > 0) { |
982 | 795 | g_autoptr(FuCborItem) item_entities = fu_cbor_item_new_array(); |
983 | 1.64k | for (guint i = 0; i < priv->entities->len; i++) { |
984 | 846 | FuCoswidFirmwareEntity *entity = g_ptr_array_index(priv->entities, i); |
985 | 846 | if (!fu_coswid_firmware_write_entity(item_entities, entity, error)) |
986 | 0 | return NULL; |
987 | 846 | } |
988 | 795 | fu_coswid_write_tag_item(root, FU_COSWID_TAG_ENTITY, item_entities); |
989 | 795 | } |
990 | | |
991 | | /* add links */ |
992 | 20.5k | if (priv->links->len > 0) { |
993 | 16.0k | g_autoptr(FuCborItem) item_links = fu_cbor_item_new_array(); |
994 | 36.3k | for (guint i = 0; i < priv->links->len; i++) { |
995 | 20.3k | FuCoswidFirmwareLink *link = g_ptr_array_index(priv->links, i); |
996 | 20.3k | g_autoptr(FuCborItem) item_link = fu_cbor_item_new_map(); |
997 | 20.3k | if (link->href != NULL) { |
998 | 109 | fu_coswid_write_tag_string(item_link, |
999 | 109 | FU_COSWID_TAG_HREF, |
1000 | 109 | link->href); |
1001 | 109 | } |
1002 | 20.3k | fu_coswid_write_tag_integer(item_link, FU_COSWID_TAG_REL, link->rel); |
1003 | 20.3k | if (!fu_cbor_item_array_append(item_links, item_link, error)) |
1004 | 0 | return NULL; |
1005 | 20.3k | } |
1006 | 16.0k | fu_coswid_write_tag_item(root, FU_COSWID_TAG_LINK, item_links); |
1007 | 16.0k | } |
1008 | | |
1009 | | /* add payloads */ |
1010 | 20.5k | if (priv->payloads->len > 0) { |
1011 | 147 | g_autoptr(FuCborItem) item_payloads = fu_cbor_item_new_array(); |
1012 | 900 | for (guint i = 0; i < priv->payloads->len; i++) { |
1013 | 753 | FuCoswidFirmwarePayload *payload = g_ptr_array_index(priv->payloads, i); |
1014 | 753 | if (!fu_coswid_firmware_write_payload(item_payloads, payload, error)) |
1015 | 0 | return NULL; |
1016 | 753 | } |
1017 | 147 | fu_coswid_write_tag_item(root, FU_COSWID_TAG_PAYLOAD, item_payloads); |
1018 | 147 | } |
1019 | | |
1020 | | /* serialize */ |
1021 | 20.5k | return fu_cbor_item_write(root, error); |
1022 | 20.5k | } |
1023 | | |
1024 | | static gboolean |
1025 | | fu_coswid_firmware_build_entity(FuCoswidFirmware *self, XbNode *n, GError **error) |
1026 | 0 | { |
1027 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1028 | 0 | const gchar *tmp; |
1029 | 0 | FuCoswidEntityRole role; |
1030 | 0 | g_autoptr(GPtrArray) roles = NULL; |
1031 | 0 | g_autoptr(FuCoswidFirmwareEntity) entity = g_new0(FuCoswidFirmwareEntity, 1); |
1032 | | |
1033 | | /* these are required */ |
1034 | 0 | tmp = xb_node_query_text(n, "name", error); |
1035 | 0 | if (tmp == NULL) { |
1036 | 0 | fwupd_error_convert(error); |
1037 | 0 | return FALSE; |
1038 | 0 | } |
1039 | 0 | entity->name = g_strdup(tmp); |
1040 | 0 | tmp = xb_node_query_text(n, "regid", error); |
1041 | 0 | if (tmp == NULL) { |
1042 | 0 | fwupd_error_convert(error); |
1043 | 0 | return FALSE; |
1044 | 0 | } |
1045 | 0 | entity->regid = g_strdup(tmp); |
1046 | | |
1047 | | /* optional */ |
1048 | 0 | roles = xb_node_query(n, "role", 0, NULL); |
1049 | 0 | if (roles != NULL) { |
1050 | 0 | for (guint i = 0; i < roles->len; i++) { |
1051 | 0 | XbNode *c = g_ptr_array_index(roles, i); |
1052 | 0 | tmp = xb_node_get_text(c); |
1053 | 0 | role = fu_coswid_entity_role_from_string(tmp); |
1054 | 0 | if (role == FU_COSWID_ENTITY_ROLE_UNKNOWN || |
1055 | 0 | role >= FU_COSWID_ENTITY_ROLE_LAST) { |
1056 | 0 | g_set_error(error, |
1057 | 0 | FWUPD_ERROR, |
1058 | 0 | FWUPD_ERROR_INVALID_DATA, |
1059 | 0 | "failed to parse entity role %s", |
1060 | 0 | tmp); |
1061 | 0 | return FALSE; |
1062 | 0 | } |
1063 | 0 | FU_BIT_SET(entity->roles, role); |
1064 | 0 | } |
1065 | 0 | } |
1066 | | |
1067 | | /* success */ |
1068 | 0 | g_ptr_array_add(priv->entities, g_steal_pointer(&entity)); |
1069 | 0 | return TRUE; |
1070 | 0 | } |
1071 | | |
1072 | | static gboolean |
1073 | | fu_coswid_firmware_build_link(FuCoswidFirmware *self, XbNode *n, GError **error) |
1074 | 0 | { |
1075 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1076 | 0 | const gchar *tmp; |
1077 | 0 | g_autoptr(FuCoswidFirmwareLink) link = g_new0(FuCoswidFirmwareLink, 1); |
1078 | | |
1079 | | /* required */ |
1080 | 0 | tmp = xb_node_query_text(n, "href", error); |
1081 | 0 | if (tmp == NULL) { |
1082 | 0 | fwupd_error_convert(error); |
1083 | 0 | return FALSE; |
1084 | 0 | } |
1085 | 0 | link->href = g_strdup(tmp); |
1086 | | |
1087 | | /* optional */ |
1088 | 0 | tmp = xb_node_query_text(n, "rel", NULL); |
1089 | 0 | if (tmp != NULL) { |
1090 | 0 | link->rel = fu_coswid_link_rel_from_string(tmp); |
1091 | 0 | if (link->rel == FU_COSWID_LINK_REL_UNKNOWN) { |
1092 | 0 | g_set_error(error, |
1093 | 0 | FWUPD_ERROR, |
1094 | 0 | FWUPD_ERROR_INVALID_DATA, |
1095 | 0 | "failed to parse link rel %s", |
1096 | 0 | tmp); |
1097 | 0 | return FALSE; |
1098 | 0 | } |
1099 | 0 | } |
1100 | | |
1101 | | /* success */ |
1102 | 0 | g_ptr_array_add(priv->links, g_steal_pointer(&link)); |
1103 | 0 | return TRUE; |
1104 | 0 | } |
1105 | | |
1106 | | static gboolean |
1107 | | fu_coswid_firmware_build_hash(FuCoswidFirmware *self, |
1108 | | XbNode *n, |
1109 | | FuCoswidFirmwarePayload *payload, |
1110 | | GError **error) |
1111 | 0 | { |
1112 | 0 | const gchar *tmp; |
1113 | 0 | g_autoptr(FuCoswidFirmwareHash) hash = g_new0(FuCoswidFirmwareHash, 1); |
1114 | | |
1115 | | /* required */ |
1116 | 0 | tmp = xb_node_query_text(n, "value", error); |
1117 | 0 | if (tmp == NULL) { |
1118 | 0 | fwupd_error_convert(error); |
1119 | 0 | return FALSE; |
1120 | 0 | } |
1121 | 0 | hash->value = fu_bytes_from_string(tmp, error); |
1122 | 0 | if (hash->value == NULL) |
1123 | 0 | return FALSE; |
1124 | | |
1125 | | /* optional */ |
1126 | 0 | tmp = xb_node_query_text(n, "alg_id", NULL); |
1127 | 0 | if (tmp != NULL) { |
1128 | 0 | hash->alg_id = fu_coswid_hash_alg_from_string(tmp); |
1129 | 0 | if (hash->alg_id == FU_COSWID_HASH_ALG_UNKNOWN) { |
1130 | 0 | g_set_error(error, |
1131 | 0 | FWUPD_ERROR, |
1132 | 0 | FWUPD_ERROR_INVALID_DATA, |
1133 | 0 | "failed to parse alg_id %s", |
1134 | 0 | tmp); |
1135 | 0 | return FALSE; |
1136 | 0 | } |
1137 | 0 | } |
1138 | | |
1139 | | /* success */ |
1140 | 0 | g_ptr_array_add(payload->hashes, g_steal_pointer(&hash)); |
1141 | 0 | return TRUE; |
1142 | 0 | } |
1143 | | |
1144 | | static gboolean |
1145 | | fu_coswid_firmware_build_payload(FuCoswidFirmware *self, XbNode *n, GError **error) |
1146 | 0 | { |
1147 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1148 | 0 | const gchar *tmp; |
1149 | 0 | guint64 tmp64; |
1150 | 0 | g_autoptr(FuCoswidFirmwarePayload) payload = fu_coswid_firmware_payload_new(); |
1151 | 0 | g_autoptr(GPtrArray) hashes = NULL; |
1152 | | |
1153 | | /* required */ |
1154 | 0 | tmp = xb_node_query_text(n, "name", NULL); |
1155 | 0 | if (tmp != NULL) |
1156 | 0 | payload->name = g_strdup(tmp); |
1157 | 0 | tmp64 = xb_node_query_text_as_uint(n, "size", NULL); |
1158 | 0 | if (tmp64 != G_MAXUINT64) |
1159 | 0 | payload->size = tmp64; |
1160 | | |
1161 | | /* multiple hashes allowed */ |
1162 | 0 | hashes = xb_node_query(n, "hash", 0, NULL); |
1163 | 0 | if (hashes != NULL) { |
1164 | 0 | for (guint i = 0; i < hashes->len; i++) { |
1165 | 0 | XbNode *c = g_ptr_array_index(hashes, i); |
1166 | 0 | if (!fu_coswid_firmware_build_hash(self, c, payload, error)) |
1167 | 0 | return FALSE; |
1168 | 0 | } |
1169 | 0 | } |
1170 | | |
1171 | | /* success */ |
1172 | 0 | g_ptr_array_add(priv->payloads, g_steal_pointer(&payload)); |
1173 | 0 | return TRUE; |
1174 | 0 | } |
1175 | | |
1176 | | static gboolean |
1177 | | fu_coswid_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) |
1178 | 0 | { |
1179 | 0 | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); |
1180 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1181 | 0 | const gchar *tmp; |
1182 | 0 | g_autoptr(GPtrArray) links = NULL; |
1183 | 0 | g_autoptr(GPtrArray) payloads = NULL; |
1184 | 0 | g_autoptr(GPtrArray) entities = NULL; |
1185 | | |
1186 | | /* simple properties */ |
1187 | 0 | tmp = xb_node_query_text(n, "product", NULL); |
1188 | 0 | if (tmp != NULL) |
1189 | 0 | priv->product = g_strdup(tmp); |
1190 | 0 | tmp = xb_node_query_text(n, "summary", NULL); |
1191 | 0 | if (tmp != NULL) |
1192 | 0 | priv->summary = g_strdup(tmp); |
1193 | 0 | tmp = xb_node_query_text(n, "colloquial_version", NULL); |
1194 | 0 | if (tmp != NULL) |
1195 | 0 | priv->colloquial_version = g_strdup(tmp); |
1196 | 0 | tmp = xb_node_query_text(n, "persistent_id", NULL); |
1197 | 0 | if (tmp != NULL) |
1198 | 0 | priv->persistent_id = g_strdup(tmp); |
1199 | 0 | tmp = xb_node_query_text(n, "device_id", NULL); |
1200 | 0 | if (tmp != NULL) |
1201 | 0 | priv->device_id = g_strdup(tmp); |
1202 | |
|
1203 | 0 | tmp = xb_node_query_text(n, "version_scheme", NULL); |
1204 | 0 | if (tmp != NULL) { |
1205 | 0 | priv->version_scheme = fu_coswid_version_scheme_from_string(tmp); |
1206 | 0 | if (priv->version_scheme == FU_COSWID_VERSION_SCHEME_UNKNOWN) { |
1207 | 0 | g_set_error(error, |
1208 | 0 | FWUPD_ERROR, |
1209 | 0 | FWUPD_ERROR_INVALID_DATA, |
1210 | 0 | "failed to parse version_scheme %s", |
1211 | 0 | tmp); |
1212 | 0 | return FALSE; |
1213 | 0 | } |
1214 | 0 | } |
1215 | | |
1216 | | /* multiple links allowed */ |
1217 | 0 | links = xb_node_query(n, "link", 0, NULL); |
1218 | 0 | if (links != NULL) { |
1219 | 0 | for (guint i = 0; i < links->len; i++) { |
1220 | 0 | XbNode *c = g_ptr_array_index(links, i); |
1221 | 0 | if (!fu_coswid_firmware_build_link(self, c, error)) |
1222 | 0 | return FALSE; |
1223 | 0 | } |
1224 | 0 | } |
1225 | | |
1226 | | /* multiple payloads allowed */ |
1227 | 0 | payloads = xb_node_query(n, "payload", 0, NULL); |
1228 | 0 | if (payloads != NULL) { |
1229 | 0 | for (guint i = 0; i < payloads->len; i++) { |
1230 | 0 | XbNode *c = g_ptr_array_index(payloads, i); |
1231 | 0 | if (!fu_coswid_firmware_build_payload(self, c, error)) |
1232 | 0 | return FALSE; |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | | /* multiple entities allowed */ |
1237 | 0 | entities = xb_node_query(n, "entity", 0, NULL); |
1238 | 0 | if (entities != NULL) { |
1239 | 0 | for (guint i = 0; i < entities->len; i++) { |
1240 | 0 | XbNode *c = g_ptr_array_index(entities, i); |
1241 | 0 | if (!fu_coswid_firmware_build_entity(self, c, error)) |
1242 | 0 | return FALSE; |
1243 | 0 | } |
1244 | 0 | } |
1245 | | |
1246 | | /* success */ |
1247 | 0 | return TRUE; |
1248 | 0 | } |
1249 | | |
1250 | | static void |
1251 | | fu_coswid_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
1252 | 0 | { |
1253 | 0 | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); |
1254 | 0 | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1255 | 0 | if (priv->version_scheme != FU_COSWID_VERSION_SCHEME_UNKNOWN) { |
1256 | 0 | fu_xmlb_builder_insert_kv(bn, |
1257 | 0 | "version_scheme", |
1258 | 0 | fu_coswid_version_scheme_to_string(priv->version_scheme)); |
1259 | 0 | } |
1260 | 0 | fu_xmlb_builder_insert_kv(bn, "product", priv->product); |
1261 | 0 | fu_xmlb_builder_insert_kv(bn, "summary", priv->summary); |
1262 | 0 | fu_xmlb_builder_insert_kv(bn, "colloquial_version", priv->colloquial_version); |
1263 | 0 | fu_xmlb_builder_insert_kv(bn, "persistent_id", priv->persistent_id); |
1264 | 0 | fu_xmlb_builder_insert_kv(bn, "device_id", priv->device_id); |
1265 | 0 | for (guint i = 0; i < priv->links->len; i++) { |
1266 | 0 | FuCoswidFirmwareLink *link = g_ptr_array_index(priv->links, i); |
1267 | 0 | g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "link", NULL); |
1268 | 0 | fu_xmlb_builder_insert_kv(bc, "href", link->href); |
1269 | 0 | if (link->rel != FU_COSWID_LINK_REL_UNKNOWN) { |
1270 | 0 | fu_xmlb_builder_insert_kv(bc, |
1271 | 0 | "rel", |
1272 | 0 | fu_coswid_link_rel_to_string(link->rel)); |
1273 | 0 | } |
1274 | 0 | } |
1275 | 0 | for (guint i = 0; i < priv->payloads->len; i++) { |
1276 | 0 | FuCoswidFirmwarePayload *payload = g_ptr_array_index(priv->payloads, i); |
1277 | 0 | g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "payload", NULL); |
1278 | 0 | fu_xmlb_builder_insert_kv(bc, "name", payload->name); |
1279 | 0 | fu_xmlb_builder_insert_kx(bc, "size", payload->size); |
1280 | 0 | for (guint j = 0; j < payload->hashes->len; j++) { |
1281 | 0 | FuCoswidFirmwareHash *hash = g_ptr_array_index(payload->hashes, j); |
1282 | 0 | g_autoptr(XbBuilderNode) bh = xb_builder_node_insert(bc, "hash", NULL); |
1283 | 0 | g_autofree gchar *value = fu_bytes_to_string(hash->value); |
1284 | 0 | fu_xmlb_builder_insert_kv(bh, |
1285 | 0 | "alg_id", |
1286 | 0 | fu_coswid_hash_alg_to_string(hash->alg_id)); |
1287 | 0 | fu_xmlb_builder_insert_kv(bh, "value", value); |
1288 | 0 | } |
1289 | 0 | } |
1290 | 0 | for (guint i = 0; i < priv->entities->len; i++) { |
1291 | 0 | FuCoswidFirmwareEntity *entity = g_ptr_array_index(priv->entities, i); |
1292 | 0 | g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "entity", NULL); |
1293 | 0 | fu_xmlb_builder_insert_kv(bc, "name", entity->name); |
1294 | 0 | fu_xmlb_builder_insert_kv(bc, "regid", entity->regid); |
1295 | 0 | for (guint j = 0; j < FU_COSWID_ENTITY_ROLE_LAST; j++) { |
1296 | 0 | if (FU_BIT_IS_SET(entity->roles, j)) { |
1297 | 0 | fu_xmlb_builder_insert_kv(bc, |
1298 | 0 | "role", |
1299 | 0 | fu_coswid_entity_role_to_string(j)); |
1300 | 0 | } |
1301 | 0 | } |
1302 | 0 | } |
1303 | 0 | } |
1304 | | |
1305 | | static void |
1306 | | fu_coswid_firmware_init(FuCoswidFirmware *self) |
1307 | 110k | { |
1308 | 110k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1309 | 110k | priv->version_scheme = FU_COSWID_VERSION_SCHEME_SEMVER; |
1310 | 110k | priv->links = g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_link_free); |
1311 | 110k | priv->payloads = |
1312 | 110k | g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_payload_free); |
1313 | 110k | priv->entities = |
1314 | 110k | g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_entity_free); |
1315 | 110k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALLOW_LINEAR); |
1316 | 110k | fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_MB); |
1317 | 110k | } |
1318 | | |
1319 | | static void |
1320 | | fu_coswid_firmware_finalize(GObject *object) |
1321 | 110k | { |
1322 | 110k | FuCoswidFirmware *self = FU_COSWID_FIRMWARE(object); |
1323 | 110k | FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); |
1324 | | |
1325 | 110k | g_free(priv->product); |
1326 | 110k | g_free(priv->summary); |
1327 | 110k | g_free(priv->colloquial_version); |
1328 | 110k | g_free(priv->persistent_id); |
1329 | 110k | g_free(priv->device_id); |
1330 | 110k | g_ptr_array_unref(priv->links); |
1331 | 110k | g_ptr_array_unref(priv->payloads); |
1332 | 110k | g_ptr_array_unref(priv->entities); |
1333 | | |
1334 | 110k | G_OBJECT_CLASS(fu_coswid_firmware_parent_class)->finalize(object); |
1335 | 110k | } |
1336 | | |
1337 | | static void |
1338 | | fu_coswid_firmware_class_init(FuCoswidFirmwareClass *klass) |
1339 | 5 | { |
1340 | 5 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
1341 | 5 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
1342 | 5 | object_class->finalize = fu_coswid_firmware_finalize; |
1343 | 5 | firmware_class->parse = fu_coswid_firmware_parse; |
1344 | 5 | firmware_class->write = fu_coswid_firmware_write; |
1345 | 5 | firmware_class->build = fu_coswid_firmware_build; |
1346 | 5 | firmware_class->export = fu_coswid_firmware_export; |
1347 | 5 | firmware_class->get_checksum = fu_coswid_firmware_get_checksum; |
1348 | 5 | } |
1349 | | |
1350 | | /** |
1351 | | * fu_coswid_firmware_new: |
1352 | | * |
1353 | | * Creates a new #FuFirmware of sub type coSWID |
1354 | | * |
1355 | | * Since: 1.8.0 |
1356 | | **/ |
1357 | | FuFirmware * |
1358 | | fu_coswid_firmware_new(void) |
1359 | 0 | { |
1360 | 0 | return FU_FIRMWARE(g_object_new(FU_TYPE_COSWID_FIRMWARE, NULL)); |
1361 | 0 | } |