]>
Commit | Line | Data |
---|---|---|
98220743 PR |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2021 Philippe Reynes <[email protected]> | |
4 | */ | |
5 | ||
98220743 PR |
6 | #include <asm/global_data.h> |
7 | DECLARE_GLOBAL_DATA_PTR; | |
8 | #include <image.h> | |
9 | #include <mapmem.h> | |
10 | ||
11 | #include <u-boot/sha256.h> | |
12 | ||
98220743 PR |
13 | /* |
14 | * Offset of the image | |
15 | * | |
16 | * This value is used to skip the header before really launching the image | |
17 | */ | |
18 | ulong image_load_offset; | |
19 | ||
20 | /* | |
21 | * This function gathers information about the signature check | |
22 | * that could be done before launching the image. | |
23 | * | |
24 | * return: | |
25 | * < 0 => an error has occurred | |
26 | * 0 => OK | |
27 | * 1 => no setup | |
28 | */ | |
29 | static int image_pre_load_sig_setup(struct image_sig_info *info) | |
30 | { | |
31 | const void *algo_name, *padding_name, *key, *mandatory; | |
32 | const u32 *sig_size; | |
33 | int key_len; | |
34 | int node, ret = 0; | |
05b0f241 | 35 | char *sig_info_path = NULL; |
98220743 PR |
36 | |
37 | if (!info) { | |
38 | log_err("ERROR: info is NULL for image pre-load sig check\n"); | |
39 | ret = -EINVAL; | |
40 | goto out; | |
41 | } | |
42 | ||
43 | memset(info, 0, sizeof(*info)); | |
44 | ||
05b0f241 SL |
45 | sig_info_path = env_get("pre_load_sig_info_path"); |
46 | if (!sig_info_path) | |
47 | sig_info_path = IMAGE_PRE_LOAD_PATH; | |
48 | ||
49 | node = fdt_path_offset(gd_fdt_blob(), sig_info_path); | |
98220743 PR |
50 | if (node < 0) { |
51 | log_info("INFO: no info for image pre-load sig check\n"); | |
52 | ret = 1; | |
53 | goto out; | |
54 | } | |
55 | ||
56 | algo_name = fdt_getprop(gd_fdt_blob(), node, | |
57 | IMAGE_PRE_LOAD_PROP_ALGO_NAME, NULL); | |
58 | if (!algo_name) { | |
59 | printf("ERROR: no algo_name for image pre-load sig check\n"); | |
60 | ret = -EINVAL; | |
61 | goto out; | |
62 | } | |
63 | ||
64 | padding_name = fdt_getprop(gd_fdt_blob(), node, | |
65 | IMAGE_PRE_LOAD_PROP_PADDING_NAME, NULL); | |
66 | if (!padding_name) { | |
67 | log_info("INFO: no padding_name provided, so using pkcs-1.5\n"); | |
68 | padding_name = "pkcs-1.5"; | |
69 | } | |
70 | ||
71 | sig_size = fdt_getprop(gd_fdt_blob(), node, | |
72 | IMAGE_PRE_LOAD_PROP_SIG_SIZE, NULL); | |
73 | if (!sig_size) { | |
74 | log_err("ERROR: no signature-size for image pre-load sig check\n"); | |
75 | ret = -EINVAL; | |
76 | goto out; | |
77 | } | |
78 | ||
79 | key = fdt_getprop(gd_fdt_blob(), node, | |
80 | IMAGE_PRE_LOAD_PROP_PUBLIC_KEY, &key_len); | |
81 | if (!key) { | |
82 | log_err("ERROR: no key for image pre-load sig check\n"); | |
83 | ret = -EINVAL; | |
84 | goto out; | |
85 | } | |
86 | ||
87 | info->algo_name = (char *)algo_name; | |
88 | info->padding_name = (char *)padding_name; | |
89 | info->key = (uint8_t *)key; | |
90 | info->key_len = key_len; | |
91 | info->sig_size = fdt32_to_cpu(*sig_size); | |
92 | ||
93 | mandatory = fdt_getprop(gd_fdt_blob(), node, | |
94 | IMAGE_PRE_LOAD_PROP_MANDATORY, NULL); | |
95 | if (mandatory && !strcmp((char *)mandatory, "yes")) | |
96 | info->mandatory = 1; | |
97 | ||
98 | /* Compute signature information */ | |
99 | info->sig_info.name = info->algo_name; | |
100 | info->sig_info.padding = image_get_padding_algo(info->padding_name); | |
101 | info->sig_info.checksum = image_get_checksum_algo(info->sig_info.name); | |
102 | info->sig_info.crypto = image_get_crypto_algo(info->sig_info.name); | |
103 | info->sig_info.key = info->key; | |
104 | info->sig_info.keylen = info->key_len; | |
105 | ||
106 | out: | |
107 | return ret; | |
108 | } | |
109 | ||
110 | static int image_pre_load_sig_get_magic(ulong addr, u32 *magic) | |
111 | { | |
112 | struct sig_header_s *sig_header; | |
113 | int ret = 0; | |
114 | ||
115 | sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); | |
116 | if (!sig_header) { | |
117 | log_err("ERROR: can't map first header\n"); | |
118 | ret = -EFAULT; | |
119 | goto out; | |
120 | } | |
121 | ||
122 | *magic = fdt32_to_cpu(sig_header->magic); | |
123 | ||
124 | unmap_sysmem(sig_header); | |
125 | ||
126 | out: | |
127 | return ret; | |
128 | } | |
129 | ||
130 | static int image_pre_load_sig_get_header_size(ulong addr, u32 *header_size) | |
131 | { | |
132 | struct sig_header_s *sig_header; | |
133 | int ret = 0; | |
134 | ||
135 | sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); | |
136 | if (!sig_header) { | |
137 | log_err("ERROR: can't map first header\n"); | |
138 | ret = -EFAULT; | |
139 | goto out; | |
140 | } | |
141 | ||
142 | *header_size = fdt32_to_cpu(sig_header->header_size); | |
143 | ||
144 | unmap_sysmem(sig_header); | |
145 | ||
146 | out: | |
147 | return ret; | |
148 | } | |
149 | ||
150 | /* | |
151 | * return: | |
152 | * < 0 => no magic and magic mandatory (or error when reading magic) | |
153 | * 0 => magic found | |
154 | * 1 => magic NOT found | |
155 | */ | |
156 | static int image_pre_load_sig_check_magic(struct image_sig_info *info, ulong addr) | |
157 | { | |
158 | u32 magic; | |
159 | int ret = 1; | |
160 | ||
161 | ret = image_pre_load_sig_get_magic(addr, &magic); | |
162 | if (ret < 0) | |
163 | goto out; | |
164 | ||
165 | if (magic != IMAGE_PRE_LOAD_SIG_MAGIC) { | |
166 | if (info->mandatory) { | |
167 | log_err("ERROR: signature is mandatory\n"); | |
168 | ret = -EINVAL; | |
169 | goto out; | |
170 | } | |
171 | ret = 1; | |
172 | goto out; | |
173 | } | |
174 | ||
175 | ret = 0; /* magic found */ | |
176 | ||
177 | out: | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static int image_pre_load_sig_check_header_sig(struct image_sig_info *info, ulong addr) | |
182 | { | |
183 | void *header; | |
184 | struct image_region reg; | |
185 | u32 sig_len; | |
186 | u8 *sig; | |
187 | int ret = 0; | |
188 | ||
189 | /* Only map header of the header and its signature */ | |
190 | header = (void *)map_sysmem(addr, SIG_HEADER_LEN + info->sig_size); | |
191 | if (!header) { | |
192 | log_err("ERROR: can't map header\n"); | |
193 | ret = -EFAULT; | |
194 | goto out; | |
195 | } | |
196 | ||
197 | reg.data = header; | |
198 | reg.size = SIG_HEADER_LEN; | |
199 | ||
200 | sig = (uint8_t *)header + SIG_HEADER_LEN; | |
201 | sig_len = info->sig_size; | |
202 | ||
203 | ret = info->sig_info.crypto->verify(&info->sig_info, ®, 1, sig, sig_len); | |
204 | if (ret) { | |
205 | log_err("ERROR: header signature check has failed (err=%d)\n", ret); | |
206 | ret = -EINVAL; | |
207 | goto out_unmap; | |
208 | } | |
209 | ||
210 | out_unmap: | |
211 | unmap_sysmem(header); | |
212 | ||
213 | out: | |
214 | return ret; | |
215 | } | |
216 | ||
217 | static int image_pre_load_sig_check_img_sig_sha256(struct image_sig_info *info, ulong addr) | |
218 | { | |
219 | struct sig_header_s *sig_header; | |
220 | u32 header_size, offset_img_sig; | |
221 | void *header; | |
222 | u8 sha256_img_sig[SHA256_SUM_LEN]; | |
223 | int ret = 0; | |
224 | ||
225 | sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); | |
226 | if (!sig_header) { | |
227 | log_err("ERROR: can't map first header\n"); | |
228 | ret = -EFAULT; | |
229 | goto out; | |
230 | } | |
231 | ||
232 | header_size = fdt32_to_cpu(sig_header->header_size); | |
233 | offset_img_sig = fdt32_to_cpu(sig_header->offset_img_sig); | |
234 | ||
235 | header = (void *)map_sysmem(addr, header_size); | |
236 | if (!header) { | |
237 | log_err("ERROR: can't map header\n"); | |
238 | ret = -EFAULT; | |
239 | goto out_sig_header; | |
240 | } | |
241 | ||
242 | sha256_csum_wd(header + offset_img_sig, info->sig_size, | |
243 | sha256_img_sig, CHUNKSZ_SHA256); | |
244 | ||
245 | ret = memcmp(sig_header->sha256_img_sig, sha256_img_sig, SHA256_SUM_LEN); | |
246 | if (ret) { | |
247 | log_err("ERROR: sha256 of image signature is invalid\n"); | |
248 | ret = -EFAULT; | |
249 | goto out_header; | |
250 | } | |
251 | ||
252 | out_header: | |
253 | unmap_sysmem(header); | |
254 | out_sig_header: | |
255 | unmap_sysmem(sig_header); | |
256 | out: | |
257 | return ret; | |
258 | } | |
259 | ||
260 | static int image_pre_load_sig_check_img_sig(struct image_sig_info *info, ulong addr) | |
261 | { | |
262 | struct sig_header_s *sig_header; | |
263 | u32 header_size, image_size, offset_img_sig; | |
264 | void *image; | |
265 | struct image_region reg; | |
266 | u32 sig_len; | |
267 | u8 *sig; | |
268 | int ret = 0; | |
269 | ||
270 | sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); | |
271 | if (!sig_header) { | |
272 | log_err("ERROR: can't map first header\n"); | |
273 | ret = -EFAULT; | |
274 | goto out; | |
275 | } | |
276 | ||
277 | header_size = fdt32_to_cpu(sig_header->header_size); | |
278 | image_size = fdt32_to_cpu(sig_header->image_size); | |
279 | offset_img_sig = fdt32_to_cpu(sig_header->offset_img_sig); | |
280 | ||
281 | unmap_sysmem(sig_header); | |
282 | ||
283 | image = (void *)map_sysmem(addr, header_size + image_size); | |
284 | if (!image) { | |
285 | log_err("ERROR: can't map full image\n"); | |
286 | ret = -EFAULT; | |
287 | goto out; | |
288 | } | |
289 | ||
290 | reg.data = image + header_size; | |
291 | reg.size = image_size; | |
292 | ||
293 | sig = (uint8_t *)image + offset_img_sig; | |
294 | sig_len = info->sig_size; | |
295 | ||
296 | ret = info->sig_info.crypto->verify(&info->sig_info, ®, 1, sig, sig_len); | |
297 | if (ret) { | |
298 | log_err("ERROR: signature check has failed (err=%d)\n", ret); | |
299 | ret = -EINVAL; | |
300 | goto out_unmap_image; | |
301 | } | |
302 | ||
303 | log_info("INFO: signature check has succeed\n"); | |
304 | ||
305 | out_unmap_image: | |
306 | unmap_sysmem(image); | |
307 | ||
308 | out: | |
309 | return ret; | |
310 | } | |
311 | ||
312 | int image_pre_load_sig(ulong addr) | |
313 | { | |
314 | struct image_sig_info info; | |
315 | int ret; | |
316 | ||
317 | ret = image_pre_load_sig_setup(&info); | |
318 | if (ret < 0) | |
319 | goto out; | |
320 | if (ret > 0) { | |
321 | ret = 0; | |
322 | goto out; | |
323 | } | |
324 | ||
325 | ret = image_pre_load_sig_check_magic(&info, addr); | |
326 | if (ret < 0) | |
327 | goto out; | |
328 | if (ret > 0) { | |
329 | ret = 0; | |
330 | goto out; | |
331 | } | |
332 | ||
333 | /* Check the signature of the signature header */ | |
334 | ret = image_pre_load_sig_check_header_sig(&info, addr); | |
335 | if (ret < 0) | |
336 | goto out; | |
337 | ||
338 | /* Check sha256 of the image signature */ | |
339 | ret = image_pre_load_sig_check_img_sig_sha256(&info, addr); | |
340 | if (ret < 0) | |
341 | goto out; | |
342 | ||
343 | /* Check the image signature */ | |
344 | ret = image_pre_load_sig_check_img_sig(&info, addr); | |
345 | if (!ret) { | |
346 | u32 header_size; | |
347 | ||
348 | ret = image_pre_load_sig_get_header_size(addr, &header_size); | |
349 | if (ret) { | |
350 | log_err("%s: can't get header size\n", __func__); | |
351 | ret = -EINVAL; | |
352 | goto out; | |
353 | } | |
354 | ||
355 | image_load_offset += header_size; | |
356 | } | |
357 | ||
358 | out: | |
359 | return ret; | |
360 | } | |
361 | ||
362 | int image_pre_load(ulong addr) | |
363 | { | |
364 | int ret = 0; | |
365 | ||
366 | image_load_offset = 0; | |
367 | ||
368 | if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD_SIG)) | |
369 | ret = image_pre_load_sig(addr); | |
370 | ||
371 | return ret; | |
372 | } |