Coverage Report

Created: 2025-07-01 07:09

/src/fwupd/plugins/synaptics-rmi/fu-synaptics-rmi-common.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2012 Andrew Duggan
3
 * Copyright 2012 Synaptics Inc.
4
 * Copyright 2019 Richard Hughes <richard@hughsie.com>
5
 *
6
 * SPDX-License-Identifier: LGPL-2.1-or-later
7
 */
8
9
#include "config.h"
10
11
#include <fwupdplugin.h>
12
13
#include <fcntl.h>
14
#include <sys/stat.h>
15
#include <sys/types.h>
16
17
#ifdef HAVE_GNUTLS
18
#include <gnutls/abstract.h>
19
#include <gnutls/crypto.h>
20
#endif
21
22
#include "fu-synaptics-rmi-common.h"
23
24
0
#define RMI_FUNCTION_QUERY_OFFSET       0
25
0
#define RMI_FUNCTION_COMMAND_OFFSET       1
26
0
#define RMI_FUNCTION_CONTROL_OFFSET       2
27
0
#define RMI_FUNCTION_DATA_OFFSET        3
28
0
#define RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET 4
29
0
#define RMI_FUNCTION_NUMBER         5
30
31
0
#define RMI_FUNCTION_VERSION_MASK     0x60
32
0
#define RMI_FUNCTION_INTERRUPT_SOURCES_MASK 0x7
33
34
#ifdef HAVE_GNUTLS
35
typedef guchar gnutls_data_t;
36
#pragma clang diagnostic push
37
#pragma clang diagnostic ignored "-Wunused-function"
38
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_data_t, gnutls_free)
39
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pubkey_t, gnutls_pubkey_deinit, NULL)
40
#pragma clang diagnostic pop
41
#endif
42
43
guint32
44
fu_synaptics_rmi_generate_checksum(const guint8 *data, gsize len)
45
1.05k
{
46
1.05k
  guint32 lsw = 0xffff;
47
1.05k
  guint32 msw = 0xffff;
48
16.6M
  for (gsize i = 0; i < len / 2; i++) {
49
16.6M
    lsw += fu_memread_uint16(&data[i * 2], G_LITTLE_ENDIAN);
50
16.6M
    msw += lsw;
51
16.6M
    lsw = (lsw & 0xffff) + (lsw >> 16);
52
16.6M
    msw = (msw & 0xffff) + (msw >> 16);
53
16.6M
  }
54
1.05k
  return msw << 16 | lsw;
55
1.05k
}
56
57
FuSynapticsRmiFunction *
58
fu_synaptics_rmi_function_parse(GByteArray *buf,
59
        guint16 page_base,
60
        guint interrupt_count,
61
        GError **error)
62
0
{
63
0
  FuSynapticsRmiFunction *func;
64
0
  guint8 interrupt_offset;
65
0
  const guint8 *data = buf->data;
66
67
  /* not expected */
68
0
  if (buf->len != RMI_DEVICE_PDT_ENTRY_SIZE) {
69
0
    g_set_error(error,
70
0
          FWUPD_ERROR,
71
0
          FWUPD_ERROR_INTERNAL,
72
0
          "PDT entry buffer invalid size %u != %i",
73
0
          buf->len,
74
0
          RMI_DEVICE_PDT_ENTRY_SIZE);
75
0
    return NULL;
76
0
  }
77
78
0
  func = g_new0(FuSynapticsRmiFunction, 1);
79
0
  func->query_base = data[RMI_FUNCTION_QUERY_OFFSET] + page_base;
80
0
  func->command_base = data[RMI_FUNCTION_COMMAND_OFFSET] + page_base;
81
0
  func->control_base = data[RMI_FUNCTION_CONTROL_OFFSET] + page_base;
82
0
  func->data_base = data[RMI_FUNCTION_DATA_OFFSET] + page_base;
83
0
  func->interrupt_source_count =
84
0
      data[RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET] & RMI_FUNCTION_INTERRUPT_SOURCES_MASK;
85
0
  func->function_number = data[RMI_FUNCTION_NUMBER];
86
0
  func->function_version =
87
0
      (data[RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET] & RMI_FUNCTION_VERSION_MASK) >> 5;
88
0
  if (func->interrupt_source_count > 0) {
89
0
    func->interrupt_reg_num = (interrupt_count + 8) / 8 - 1;
90
    /* set an enable bit for each data source */
91
0
    interrupt_offset = interrupt_count % 8;
92
0
    func->interrupt_mask = 0;
93
0
    for (guint i = interrupt_offset;
94
0
         i < (func->interrupt_source_count + interrupt_offset);
95
0
         i++)
96
0
      FU_BIT_SET(func->interrupt_mask, i);
97
0
  }
98
0
  return func;
99
0
}
100
101
gboolean
102
fu_synaptics_rmi_device_writeln(const gchar *fn, const gchar *buf, GError **error)
103
0
{
104
0
  g_autoptr(FuIOChannel) io = NULL;
105
0
  io = fu_io_channel_new_file(fn, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error);
106
0
  if (io == NULL)
107
0
    return FALSE;
108
0
  return fu_io_channel_write_raw(io,
109
0
               (const guint8 *)buf,
110
0
               strlen(buf),
111
0
               1000,
112
0
               FU_IO_CHANNEL_FLAG_NONE,
113
0
               error);
114
0
}
115
116
gboolean
117
fu_synaptics_rmi_verify_sha256_signature(GBytes *payload,
118
           GBytes *pubkey,
119
           GBytes *signature,
120
           GError **error)
121
0
{
122
#ifdef HAVE_GNUTLS
123
  gnutls_datum_t hash;
124
  gnutls_datum_t m;
125
  gnutls_datum_t e;
126
  gnutls_datum_t sig;
127
  gnutls_hash_hd_t sha2;
128
  g_auto(gnutls_pubkey_t) pub = NULL;
129
  gint ec;
130
  guint8 exponent[] = {1, 0, 1};
131
  guint hash_length = gnutls_hash_get_len(GNUTLS_DIG_SHA256);
132
  g_autoptr(gnutls_data_t) hash_data = NULL;
133
134
  /* hash firmware data */
135
  hash_data = gnutls_malloc(hash_length);
136
  gnutls_hash_init(&sha2, GNUTLS_DIG_SHA256);
137
  gnutls_hash(sha2, g_bytes_get_data(payload, NULL), g_bytes_get_size(payload));
138
  gnutls_hash_deinit(sha2, hash_data);
139
140
  /* hash */
141
  hash.size = hash_length;
142
  hash.data = hash_data;
143
144
  /* modulus */
145
  m.size = g_bytes_get_size(pubkey);
146
  m.data = (guint8 *)g_bytes_get_data(pubkey, NULL);
147
148
  /* exponent */
149
  e.size = sizeof(exponent);
150
  e.data = exponent;
151
152
  /* signature */
153
  sig.size = g_bytes_get_size(signature);
154
  sig.data = (guint8 *)g_bytes_get_data(signature, NULL);
155
156
  gnutls_pubkey_init(&pub);
157
  ec = gnutls_pubkey_import_rsa_raw(pub, &m, &e);
158
  if (ec < 0) {
159
    g_set_error(error,
160
          FWUPD_ERROR,
161
          FWUPD_ERROR_NOT_SUPPORTED,
162
          "failed to import RSA key: %s",
163
          gnutls_strerror(ec));
164
    return FALSE;
165
  }
166
  ec = gnutls_pubkey_verify_hash2(pub, GNUTLS_SIGN_RSA_SHA256, 0, &hash, &sig);
167
  if (ec < 0) {
168
    g_set_error(error,
169
          FWUPD_ERROR,
170
          FWUPD_ERROR_NOT_SUPPORTED,
171
          "failed to verify firmware: %s",
172
          gnutls_strerror(ec));
173
    return FALSE;
174
  }
175
#endif
176
  /* success */
177
0
  return TRUE;
178
0
}