Coverage Report

Created: 2026-07-16 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-tpm-eventlog-v2.c
Line
Count
Source
1
/*
2
 * Copyright 2019 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#define G_LOG_DOMAIN "FuTpmEventlog"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-common.h"
13
#include "fu-input-stream.h"
14
#include "fu-tpm-eventlog-common.h"
15
#include "fu-tpm-eventlog-item.h"
16
#include "fu-tpm-eventlog-v2.h"
17
#include "fu-tpm-struct.h"
18
19
/**
20
 * FuTpmEventlogV2:
21
 *
22
 * Parse the TPM eventlog.
23
 */
24
25
struct _FuTpmEventlogV2 {
26
  FuFirmware parent_instance;
27
};
28
29
0
G_DEFINE_TYPE(FuTpmEventlogV2, fu_tpm_eventlog_v2, FU_TYPE_TPM_EVENTLOG)
30
0
31
0
static guint32
32
0
fu_tpm_eventlog_v2_hash_get_size(FuTpmAlg hash_kind)
33
0
{
34
0
  if (hash_kind == FU_TPM_ALG_SHA1)
35
0
    return FU_TPM_DIGEST_SIZE_SHA1;
36
0
  if (hash_kind == FU_TPM_ALG_SHA256)
37
0
    return FU_TPM_DIGEST_SIZE_SHA256;
38
0
  if (hash_kind == FU_TPM_ALG_SHA384)
39
0
    return FU_TPM_DIGEST_SIZE_SHA384;
40
0
  if (hash_kind == FU_TPM_ALG_SHA512)
41
0
    return FU_TPM_DIGEST_SIZE_SHA512;
42
0
  if (hash_kind == FU_TPM_ALG_SM3_256)
43
0
    return FU_TPM_DIGEST_SIZE_SM3_256;
44
0
  return 0;
45
0
}
46
47
static gboolean
48
fu_tpm_eventlog_v2_parse_item(FuTpmEventlogV2 *self,
49
            FuInputStream *stream,
50
            gsize *idx,
51
            GError **error)
52
0
{
53
0
  guint32 pcr;
54
0
  guint32 digestcnt;
55
0
  guint32 datasz = 0;
56
0
  g_autoptr(GBytes) checksum_sha1 = NULL;
57
0
  g_autoptr(GBytes) checksum_sha256 = NULL;
58
0
  g_autoptr(GBytes) checksum_sha384 = NULL;
59
0
  g_autoptr(FuStructTpmEventLog2) st = NULL;
60
0
  g_autoptr(FuTpmEventlogItem) item = NULL;
61
62
  /* read checksum block */
63
0
  st = fu_struct_tpm_event_log2_parse_stream(stream, *idx, error);
64
0
  if (st == NULL)
65
0
    return FALSE;
66
0
  *idx += st->buf->len;
67
0
  digestcnt = fu_struct_tpm_event_log2_get_digest_count(st);
68
0
  for (guint i = 0; i < digestcnt; i++) {
69
0
    guint16 alg_type = 0;
70
0
    guint32 alg_size = 0;
71
0
    g_autoptr(GBytes) checksum = NULL;
72
73
    /* get checksum type */
74
0
    if (!fu_input_stream_read_u16(stream, *idx, &alg_type, G_LITTLE_ENDIAN, error))
75
0
      return FALSE;
76
0
    alg_size = fu_tpm_eventlog_v2_hash_get_size(alg_type);
77
0
    if (alg_size == 0) {
78
0
      g_set_error(error,
79
0
            FWUPD_ERROR,
80
0
            FWUPD_ERROR_NOT_SUPPORTED,
81
0
            "hash algorithm 0x%x size not known",
82
0
            alg_type);
83
0
      return FALSE;
84
0
    }
85
86
    /* build checksum */
87
0
    *idx += sizeof(alg_type);
88
89
    /* copy hash */
90
0
    checksum = fu_input_stream_read_bytes(stream, *idx, alg_size, NULL, error);
91
0
    if (checksum == NULL)
92
0
      return FALSE;
93
94
    /* save this for analysis */
95
0
    if (alg_type == FU_TPM_ALG_SHA1)
96
0
      checksum_sha1 = g_bytes_ref(checksum);
97
0
    else if (alg_type == FU_TPM_ALG_SHA256)
98
0
      checksum_sha256 = g_bytes_ref(checksum);
99
0
    else if (alg_type == FU_TPM_ALG_SHA384)
100
0
      checksum_sha384 = g_bytes_ref(checksum);
101
102
    /* next block */
103
0
    *idx += alg_size;
104
0
  }
105
106
  /* read data block */
107
0
  if (!fu_input_stream_read_u32(stream, *idx, &datasz, G_LITTLE_ENDIAN, error))
108
0
    return FALSE;
109
0
  if (datasz > FU_MB) {
110
0
    g_set_error_literal(error,
111
0
            FWUPD_ERROR,
112
0
            FWUPD_ERROR_NOT_SUPPORTED,
113
0
            "event log item too large");
114
0
    return FALSE;
115
0
  }
116
117
  /* save blob */
118
0
  *idx += sizeof(datasz);
119
0
  pcr = fu_struct_tpm_event_log2_get_pcr(st);
120
121
  /* build item */
122
0
  item = fu_tpm_eventlog_item_new();
123
0
  fu_tpm_eventlog_item_set_pcr(item, pcr);
124
0
  fu_tpm_eventlog_item_set_kind(item, fu_struct_tpm_event_log2_get_type(st));
125
0
  if (checksum_sha1 != NULL)
126
0
    fu_tpm_eventlog_item_add_checksum(item, FU_TPM_ALG_SHA1, checksum_sha1);
127
0
  if (checksum_sha256 != NULL)
128
0
    fu_tpm_eventlog_item_add_checksum(item, FU_TPM_ALG_SHA256, checksum_sha256);
129
0
  if (checksum_sha384 != NULL)
130
0
    fu_tpm_eventlog_item_add_checksum(item, FU_TPM_ALG_SHA384, checksum_sha384);
131
0
  if (datasz > 0) {
132
0
    g_autoptr(GBytes) blob = NULL;
133
0
    blob = fu_input_stream_read_bytes(stream, *idx, datasz, NULL, error);
134
0
    if (blob == NULL)
135
0
      return FALSE;
136
0
    fu_firmware_set_bytes(FU_FIRMWARE(item), blob);
137
0
  }
138
0
  if (!fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(item), error))
139
0
    return FALSE;
140
141
  /* next entry */
142
0
  *idx += datasz;
143
0
  return TRUE;
144
0
}
145
146
static gboolean
147
fu_tpm_eventlog_v2_parse(FuFirmware *firmware,
148
       FuInputStream *stream,
149
       FuFirmwareParseFlags flags,
150
       GError **error)
151
0
{
152
0
  FuTpmEventlogV2 *self = FU_TPM_EVENTLOG_V2(firmware);
153
0
  guint32 hdrsz = 0x0;
154
0
  gsize streamsz = 0;
155
0
  g_autoptr(FuStructTpmEventLog2Hdr) st_hdr = NULL;
156
157
  /* look for TCG v2 signature */
158
0
  st_hdr = fu_struct_tpm_event_log2_hdr_parse_stream(stream, 0x0, error);
159
0
  if (st_hdr == NULL)
160
0
    return FALSE;
161
162
  /* advance over the header block */
163
0
  hdrsz = fu_struct_tpm_event_log2_hdr_get_datasz(st_hdr);
164
0
  if (!fu_input_stream_size(stream, &streamsz, error))
165
0
    return FALSE;
166
0
  for (gsize idx = FU_STRUCT_TPM_EVENT_LOG1_ITEM_SIZE + hdrsz; idx < streamsz;) {
167
0
    if (!fu_tpm_eventlog_v2_parse_item(self, stream, &idx, error))
168
0
      return FALSE;
169
0
  }
170
171
  /* success */
172
0
  return TRUE;
173
0
}
174
175
static GByteArray *
176
fu_tpm_eventlog_v2_write_item(FuTpmEventlogItem *item, GError **error)
177
0
{
178
0
  guint32 digest_count = 0;
179
0
  g_autoptr(FuStructTpmEventLog2) st = fu_struct_tpm_event_log2_new();
180
0
  g_autoptr(GBytes) blob = NULL;
181
0
  g_autoptr(GBytes) digest_sha1 = NULL;
182
0
  g_autoptr(GBytes) digest_sha256 = NULL;
183
0
  g_autoptr(GBytes) digest_sha384 = NULL;
184
185
  /* get all the checksums we know about */
186
0
  digest_sha1 = fu_tpm_eventlog_item_get_checksum(item, FU_TPM_ALG_SHA1, NULL);
187
0
  if (digest_sha1 != NULL)
188
0
    digest_count++;
189
0
  digest_sha256 = fu_tpm_eventlog_item_get_checksum(item, FU_TPM_ALG_SHA256, NULL);
190
0
  if (digest_sha256 != NULL)
191
0
    digest_count++;
192
0
  digest_sha384 = fu_tpm_eventlog_item_get_checksum(item, FU_TPM_ALG_SHA384, NULL);
193
0
  if (digest_sha384 != NULL)
194
0
    digest_count++;
195
196
  /* write struct */
197
0
  fu_struct_tpm_event_log2_set_pcr(st, fu_tpm_eventlog_item_get_pcr(item));
198
0
  fu_struct_tpm_event_log2_set_type(st, fu_tpm_eventlog_item_get_kind(item));
199
0
  fu_struct_tpm_event_log2_set_digest_count(st, digest_count);
200
0
  if (digest_sha1 != NULL) {
201
0
    fu_byte_array_append_uint16(st->buf, FU_TPM_ALG_SHA1, G_LITTLE_ENDIAN);
202
0
    fu_byte_array_append_bytes(st->buf, digest_sha1);
203
0
  }
204
0
  if (digest_sha256 != NULL) {
205
0
    fu_byte_array_append_uint16(st->buf, FU_TPM_ALG_SHA256, G_LITTLE_ENDIAN);
206
0
    fu_byte_array_append_bytes(st->buf, digest_sha256);
207
0
  }
208
0
  if (digest_sha384 != NULL) {
209
0
    fu_byte_array_append_uint16(st->buf, FU_TPM_ALG_SHA384, G_LITTLE_ENDIAN);
210
0
    fu_byte_array_append_bytes(st->buf, digest_sha384);
211
0
  }
212
213
  /* write data */
214
0
  blob = fu_firmware_get_bytes(FU_FIRMWARE(item), error);
215
0
  if (blob == NULL)
216
0
    return NULL;
217
0
  fu_byte_array_append_uint32(st->buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN);
218
0
  fu_byte_array_append_bytes(st->buf, blob);
219
220
  /* done, dump */
221
0
  return g_steal_pointer(&st->buf);
222
0
}
223
224
static GByteArray *
225
fu_tpm_eventlog_v2_write(FuFirmware *firmware, GError **error)
226
0
{
227
0
  g_autoptr(GByteArray) buf = g_byte_array_new();
228
0
  g_autoptr(GPtrArray) items = fu_firmware_get_images(firmware);
229
0
  g_autoptr(FuStructTpmEventLog2Hdr) st_hdr = fu_struct_tpm_event_log2_hdr_new();
230
231
  /* header */
232
0
  g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len);
233
234
0
  for (guint i = 0; i < items->len; i++) {
235
0
    FuTpmEventlogItem *item = g_ptr_array_index(items, i);
236
0
    g_autoptr(GByteArray) buf_item = NULL;
237
0
    buf_item = fu_tpm_eventlog_v2_write_item(item, error);
238
0
    if (buf_item == NULL)
239
0
      return NULL;
240
0
    g_byte_array_append(buf, buf_item->data, buf_item->len);
241
0
  }
242
243
  /* success */
244
0
  return g_steal_pointer(&buf);
245
0
}
246
247
static void
248
fu_tpm_eventlog_v2_class_init(FuTpmEventlogV2Class *klass)
249
0
{
250
0
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
251
0
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
252
0
  firmware_class->parse = fu_tpm_eventlog_v2_parse;
253
0
  firmware_class->write = fu_tpm_eventlog_v2_write;
254
0
  fu_firmware_add_image_gtype(firmware_class, FU_TYPE_TPM_EVENTLOG_ITEM);
255
0
}
256
257
static void
258
fu_tpm_eventlog_v2_init(FuTpmEventlogV2 *self)
259
0
{
260
0
}
261
262
/**
263
 * fu_tpm_eventlog_v2_new:
264
 *
265
 * Creates a new object to parse TPMv2 eventlog data.
266
 *
267
 * Returns: a #FuTpmEventlog
268
 *
269
 * Since: 2.1.1
270
 **/
271
FuTpmEventlog *
272
fu_tpm_eventlog_v2_new(void)
273
0
{
274
0
  FuTpmEventlogV2 *self;
275
0
  self = g_object_new(FU_TYPE_TPM_EVENTLOG_V2, NULL);
276
0
  return FU_TPM_EVENTLOG(self);
277
0
}