/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 | } |