Coverage Report

Created: 2026-04-09 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/synaptics-rmi/fu-synaptics-rmi-common.c
Line
Count
Source
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
0
{
46
0
  guint32 lsw = 0xffff;
47
0
  guint32 msw = 0xffff;
48
0
  for (gsize i = 0; i < len / 2; i++) {
49
0
    lsw += fu_memread_uint16(&data[i * 2], G_LITTLE_ENDIAN);
50
0
    msw += lsw;
51
0
    lsw = (lsw & 0xffff) + (lsw >> 16);
52
0
    msw = (msw & 0xffff) + (msw >> 16);
53
0
  }
54
0
  return msw << 16 | lsw;
55
0
}
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_verify_sha256_signature(GBytes *payload,
103
           GBytes *pubkey,
104
           GBytes *signature,
105
           GError **error)
106
0
{
107
#ifdef HAVE_GNUTLS
108
  gnutls_datum_t hash;
109
  gnutls_datum_t m;
110
  gnutls_datum_t e;
111
  gnutls_datum_t sig;
112
  gnutls_hash_hd_t sha2;
113
  g_auto(gnutls_pubkey_t) pub = NULL;
114
  gint ec;
115
  guint8 exponent[] = {1, 0, 1};
116
  guint hash_length = gnutls_hash_get_len(GNUTLS_DIG_SHA256);
117
  g_autoptr(gnutls_data_t) hash_data = NULL;
118
119
  /* hash firmware data */
120
  hash_data = gnutls_malloc(hash_length);
121
  gnutls_hash_init(&sha2, GNUTLS_DIG_SHA256);
122
  gnutls_hash(sha2, g_bytes_get_data(payload, NULL), g_bytes_get_size(payload));
123
  gnutls_hash_deinit(sha2, hash_data);
124
125
  /* hash */
126
  hash.size = hash_length;
127
  hash.data = hash_data;
128
129
  /* modulus */
130
  m.size = g_bytes_get_size(pubkey);
131
  m.data = (guint8 *)g_bytes_get_data(pubkey, NULL);
132
133
  /* exponent */
134
  e.size = sizeof(exponent);
135
  e.data = exponent;
136
137
  /* signature */
138
  sig.size = g_bytes_get_size(signature);
139
  sig.data = (guint8 *)g_bytes_get_data(signature, NULL);
140
141
  gnutls_pubkey_init(&pub);
142
  ec = gnutls_pubkey_import_rsa_raw(pub, &m, &e);
143
  if (ec < 0) {
144
    g_set_error(error,
145
          FWUPD_ERROR,
146
          FWUPD_ERROR_NOT_SUPPORTED,
147
          "failed to import RSA key: %s",
148
          gnutls_strerror(ec));
149
    return FALSE;
150
  }
151
  ec = gnutls_pubkey_verify_hash2(pub, GNUTLS_SIGN_RSA_SHA256, 0, &hash, &sig);
152
  if (ec < 0) {
153
    g_set_error(error,
154
          FWUPD_ERROR,
155
          FWUPD_ERROR_NOT_SUPPORTED,
156
          "failed to verify firmware: %s",
157
          gnutls_strerror(ec));
158
    return FALSE;
159
  }
160
#endif
161
  /* success */
162
0
  return TRUE;
163
0
}