blob: 75d8b70b5971b75a4535edb42a27a9a8484f0e7e [file] [log] [blame]
Nan Zhou7a337042021-07-26 21:05:21 -07001/*
2 * Copyright 2021 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
William A. Kennington III5acaca22021-10-28 16:32:33 -070016#include <libcr51sign/libcr51sign.h>
Nan Zhou7a337042021-07-26 21:05:21 -070017#include <stdio.h>
Nan Zhou7a337042021-07-26 21:05:21 -070018#include <string.h>
19
20#ifdef __cplusplus
21extern "C"
22{
23#endif
24
25#ifndef USER_PRINT
William A. Kennington IIIdeb55012021-10-28 17:12:23 -070026#define CPRINTS(ctx, ...) fprintf(stderr, __VA_ARGS__)
Nan Zhou7a337042021-07-26 21:05:21 -070027#endif
28
29#define MEMBER_SIZE(type, field) sizeof(((type*)0)->field)
30
31// True of x is a power of two
32#define POWER_OF_TWO(x) ((x) && !((x) & ((x)-1)))
33
34// Maximum version supported. Major revisions are not backwards compatible.
35#define MAX_MAJOR_VERSION 1
36
Nan Zhou7a337042021-07-26 21:05:21 -070037// Descriptor alignment on the external EEPROM.
38#define DESCRIPTOR_ALIGNMENT (64 * 1024)
39
40// SPS EEPROM sector size is 4KiB, since this is the smallest erasable size.
41#define IMAGE_REGION_ALIGNMENT 4096
42
43#define MAX_READ_SIZE 1024
44
45#ifndef ARRAY_SIZE
46#define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0]))
47#endif
48
49// Values of SIGNATURE_OFFSET shuold be same for all sig types (2048,3072,4096)
50#define SIGNATURE_OFFSET offsetof(struct signature_rsa3072_pkcs15, modulus)
51
52#ifndef BUILD_ASSERT
53#define BUILD_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)]))
54#endif
55
Patrick Williams60849572023-10-20 11:19:52 -050056typedef enum libcr51sign_validation_failure_reason failure_reason;
Nan Zhou7a337042021-07-26 21:05:21 -070057
Patrick Williams60849572023-10-20 11:19:52 -050058// Returns the bytes size of keys used in the given signature_scheme.
59// Return error if signature_scheme is invalid.
60//
61static failure_reason get_key_size(enum signature_scheme signature_scheme,
62 uint16_t* key_size)
63{
64 switch (signature_scheme)
Nan Zhou7a337042021-07-26 21:05:21 -070065 {
Patrick Williams60849572023-10-20 11:19:52 -050066 case SIGNATURE_RSA2048_PKCS15:
67 *key_size = 256;
68 return LIBCR51SIGN_SUCCESS;
69 case SIGNATURE_RSA3072_PKCS15:
70 *key_size = 384;
71 return LIBCR51SIGN_SUCCESS;
72 case SIGNATURE_RSA4096_PKCS15:
73 case SIGNATURE_RSA4096_PKCS15_SHA512:
74 *key_size = 512;
75 return LIBCR51SIGN_SUCCESS;
76 default:
77 return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
78 }
79}
80
81// Returns the hash_type for a given signature scheme
82// Returns error if scheme is invalid.
83failure_reason get_hash_type_from_signature(enum signature_scheme scheme,
84 enum hash_type* type)
85{
86 switch (scheme)
87 {
88 case SIGNATURE_RSA2048_PKCS15:
89 case SIGNATURE_RSA3072_PKCS15:
90 case SIGNATURE_RSA4096_PKCS15:
91 *type = HASH_SHA2_256;
92 return LIBCR51SIGN_SUCCESS;
93 case SIGNATURE_RSA4096_PKCS15_SHA512:
94 *type = HASH_SHA2_512;
95 return LIBCR51SIGN_SUCCESS;
96 default:
97 return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
98 }
99}
100
101// Check if the given hash_type is supported.
102// Returns error if hash_type is not supported.
103static failure_reason is_hash_type_supported(enum hash_type type)
104{
105 switch (type)
106 {
107 case HASH_SHA2_256:
108 case HASH_SHA2_512:
109 return LIBCR51SIGN_SUCCESS;
110 default:
111 return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
112 }
113}
114
115// Determines digest size for a given hash_type.
116// Returns error if hash_type is not supported.
117static failure_reason get_hash_digest_size(enum hash_type type, uint32_t* size)
118{
119 switch (type)
120 {
121 case HASH_SHA2_256:
122 *size = LIBCR51SIGN_SHA256_DIGEST_SIZE;
123 return LIBCR51SIGN_SUCCESS;
124 case HASH_SHA2_512:
125 *size = LIBCR51SIGN_SHA512_DIGEST_SIZE;
126 return LIBCR51SIGN_SUCCESS;
127 default:
128 return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
129 }
130}
131
132// Determines hash struct size for a given hash_type.
133// Returns error if hash_type is not supported.
134static failure_reason get_hash_struct_size(enum hash_type type, uint32_t* size)
135{
136 switch (type)
137 {
138 case HASH_SHA2_256:
139 *size = sizeof(struct hash_sha256);
140 return LIBCR51SIGN_SUCCESS;
141 case HASH_SHA2_512:
142 *size = sizeof(struct hash_sha256);
143 return LIBCR51SIGN_SUCCESS;
144 default:
145 return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
146 }
147}
148
149// Checks that:
150// - The signing key is trusted
151// - The target version is not denylisted
152// If validating a staged update, also checks that:
153// - The target image family matches the current image family
154// - The image type transition is legal (i.e. dev -> *|| prod -> prod) or
155// alternatively that the hardware ID is allowlisted
156// Assuming the caller has performed following:
157// board_get_base_key_index();
158// board_get_key_array
159// Possible return codes:
160// LIBCR51SIGN_SUCCESS = 0,
161// LIBCR51SIGN_ERROR_RUNTIME_FAILURE = 1,
162// LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR = 3,
163// LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY = 4,
164// LIBCR51SIGN_ERROR_IMAGE_TYPE_DISALLOWED = 5,
165
166static failure_reason validate_transition(const struct libcr51sign_ctx* ctx,
167 const struct libcr51sign_intf* intf,
168 uint32_t signature_struct_offset)
169{
170 BUILD_ASSERT((offsetof(struct signature_rsa2048_pkcs15, modulus) ==
171 SIGNATURE_OFFSET &&
172 offsetof(struct signature_rsa3072_pkcs15, modulus) ==
173 SIGNATURE_OFFSET &&
174 offsetof(struct signature_rsa4096_pkcs15, modulus) ==
175 SIGNATURE_OFFSET));
176
177 // Read up to the modulus.
178 enum
179 {
180 read_len = SIGNATURE_OFFSET
181 };
182 uint8_t buffer[read_len];
183 int rv;
184 rv = intf->read(ctx, signature_struct_offset, read_len, buffer);
185 if (rv != LIBCR51SIGN_SUCCESS)
186 {
187 CPRINTS(ctx, "validate_transition: failed to read signature struct\n");
188 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
189 }
190 if (*(uint32_t*)buffer != SIGNATURE_MAGIC)
191 {
192 CPRINTS(ctx, "validate_transition: bad signature magic\n");
193 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
Nan Zhou7a337042021-07-26 21:05:21 -0700194 }
195
Patrick Williams60849572023-10-20 11:19:52 -0500196 if (ctx->descriptor.image_family != ctx->current_image_family &&
197 ctx->descriptor.image_family != IMAGE_FAMILY_ALL &&
198 ctx->current_image_family != IMAGE_FAMILY_ALL)
Nan Zhou7a337042021-07-26 21:05:21 -0700199 {
Patrick Williams60849572023-10-20 11:19:52 -0500200 CPRINTS(ctx, "validate_transition: invalid image family\n");
201 return LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY;
Nan Zhou7a337042021-07-26 21:05:21 -0700202 }
203
Patrick Williams60849572023-10-20 11:19:52 -0500204 if (intf->is_production_mode == NULL)
Nan Zhou7a337042021-07-26 21:05:21 -0700205 {
Patrick Williams60849572023-10-20 11:19:52 -0500206 CPRINTS(ctx, "validate_transition: missing is_production_mode\n");
207 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
208 }
209 if (intf->is_production_mode() && (ctx->descriptor.image_type == IMAGE_DEV))
210 {
211 CPRINTS(ctx, "validate_transition: checking exemption allowlist\n");
212
213 // If function is NULL or if the function call return false, return
214 // error
215 if (intf->prod_to_dev_downgrade_allowed == NULL ||
216 !intf->prod_to_dev_downgrade_allowed())
Nan Zhou7a337042021-07-26 21:05:21 -0700217 {
Patrick Williams60849572023-10-20 11:19:52 -0500218 CPRINTS(ctx, "validate_transition: illegal image type\n");
219 return LIBCR51SIGN_ERROR_DEV_DOWNGRADE_DISALLOWED;
Nan Zhou7a337042021-07-26 21:05:21 -0700220 }
221 }
Patrick Williams60849572023-10-20 11:19:52 -0500222 return LIBCR51SIGN_SUCCESS;
223}
Nan Zhou7a337042021-07-26 21:05:21 -0700224
Patrick Williams60849572023-10-20 11:19:52 -0500225// If caller had provided read_and_hash_update call that, otherwise call
226// read and then update.
227
228static failure_reason read_and_hash_update(const struct libcr51sign_ctx* ctx,
229 const struct libcr51sign_intf* intf,
230 uint32_t offset, uint32_t size)
231{
232 uint8_t read_buffer[MAX_READ_SIZE];
233 int rv;
234 int read_size;
235
236 if (intf->read_and_hash_update)
Nan Zhou7a337042021-07-26 21:05:21 -0700237 {
Patrick Williams60849572023-10-20 11:19:52 -0500238 rv = intf->read_and_hash_update((void*)ctx, offset, size);
Nan Zhou7a337042021-07-26 21:05:21 -0700239 }
Patrick Williams60849572023-10-20 11:19:52 -0500240 else
Nan Zhou7a337042021-07-26 21:05:21 -0700241 {
Patrick Williams60849572023-10-20 11:19:52 -0500242 if (!intf->hash_update)
Nan Zhou7a337042021-07-26 21:05:21 -0700243 {
Patrick Williams60849572023-10-20 11:19:52 -0500244 CPRINTS(ctx, "read_and_hash_update: missing hash_update\n");
Nan Zhou7a337042021-07-26 21:05:21 -0700245 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
246 }
Patrick Williams60849572023-10-20 11:19:52 -0500247 do
Nan Zhou7a337042021-07-26 21:05:21 -0700248 {
Patrick Williams60849572023-10-20 11:19:52 -0500249 read_size = size < MAX_READ_SIZE ? size : MAX_READ_SIZE;
250 rv = intf->read((void*)ctx, offset, read_size, read_buffer);
251 if (rv != LIBCR51SIGN_SUCCESS)
Nan Zhou7a337042021-07-26 21:05:21 -0700252 {
Patrick Williams60849572023-10-20 11:19:52 -0500253 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
Nan Zhou7a337042021-07-26 21:05:21 -0700254 }
Patrick Williams60849572023-10-20 11:19:52 -0500255 rv = intf->hash_update((void*)ctx, read_buffer, read_size);
256 if (rv != LIBCR51SIGN_SUCCESS)
257 {
258 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
259 }
260 offset += read_size;
261 size -= read_size;
262 } while (size > 0);
263 }
264 return rv;
265}
266
267// Validates the image_region array, namely that:
268// - The regions are aligned, contiguous & exhaustive
269// - That the image descriptor resides in a static region
270//
271// If the array is consistent, proceeds to hash the static regions and
272// validates the hash. d_offset is the absolute image descriptor offset
273
274static failure_reason validate_payload_regions(
275 const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
276 uint32_t d_offset, struct libcr51sign_validated_regions* image_regions)
277{
278 // Allocate buffer to accomodate largest supported hash-type(SHA512)
279 uint8_t magic_and_digest[MEMBER_SIZE(struct hash_sha512, hash_magic) +
280 LIBCR51SIGN_SHA512_DIGEST_SIZE];
281 uint8_t dcrypto_digest[LIBCR51SIGN_SHA512_DIGEST_SIZE];
282 uint32_t byte_count, region_count, image_size, hash_offset, digest_size;
283 uint32_t i;
284 uint8_t d_region_num = 0;
285 int rv;
286 const struct image_region* region;
287
288 if (image_regions == NULL)
289 {
290 CPRINTS(ctx, "Missing image region input\n");
291 return LIBCR51SIGN_ERROR_INVALID_REGION_INPUT;
Nan Zhou7a337042021-07-26 21:05:21 -0700292 }
293
Patrick Williams60849572023-10-20 11:19:52 -0500294 BUILD_ASSERT((MEMBER_SIZE(struct hash_sha256, hash_magic) ==
295 MEMBER_SIZE(struct hash_sha512, hash_magic)));
296 image_size = ctx->descriptor.image_size;
297 region_count = ctx->descriptor.region_count;
298 hash_offset = d_offset + sizeof(struct image_descriptor) +
299 region_count * sizeof(struct image_region);
300 // Read the image_region array.
Nan Zhou7a337042021-07-26 21:05:21 -0700301
Patrick Williams60849572023-10-20 11:19:52 -0500302 if (region_count > ARRAY_SIZE(image_regions->image_regions))
Nan Zhou7a337042021-07-26 21:05:21 -0700303 {
Patrick Williams60849572023-10-20 11:19:52 -0500304 CPRINTS(ctx, "validate_payload_regions: "
305 "ctx->descriptor.region_count is greater "
306 "than LIBCR51SIGN_MAX_REGION_COUNT\n");
307 return LIBCR51SIGN_ERROR_INVALID_REGION_SIZE;
308 }
Nan Zhou7a337042021-07-26 21:05:21 -0700309
Patrick Williams60849572023-10-20 11:19:52 -0500310 rv = intf->read(ctx, d_offset + sizeof(struct image_descriptor),
311 region_count * sizeof(struct image_region),
312 (uint8_t*)&image_regions->image_regions);
313
314 image_regions->region_count = region_count;
315
316 if (rv != LIBCR51SIGN_SUCCESS)
317 {
318 CPRINTS(ctx, "validate_payload_regions: failed to read region array\n");
319 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
320 }
321
322 // Validate that the regions are contiguous & exhaustive.
323 for (i = 0, byte_count = 0; i < region_count; i++)
324 {
325 region = image_regions->image_regions + i;
326
327 CPRINTS(ctx, "validate_payload_regions: region #%d \"%s\" (%x - %x)\n",
328 i, (const char*)region->region_name, region->region_offset,
329 region->region_offset + region->region_size);
330 if ((region->region_offset % IMAGE_REGION_ALIGNMENT) != 0 ||
331 (region->region_size % IMAGE_REGION_ALIGNMENT) != 0)
Nan Zhou7a337042021-07-26 21:05:21 -0700332 {
Patrick Williams60849572023-10-20 11:19:52 -0500333 CPRINTS(ctx, "validate_payload_regions: regions must be sector "
334 "aligned\n");
335 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
Nan Zhou7a337042021-07-26 21:05:21 -0700336 }
Patrick Williams60849572023-10-20 11:19:52 -0500337 if (region->region_offset != byte_count ||
338 region->region_size > image_size - byte_count)
Nan Zhou7a337042021-07-26 21:05:21 -0700339 {
Patrick Williams60849572023-10-20 11:19:52 -0500340 CPRINTS(ctx, "validate_payload_regions: invalid region array\n");
341 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
342 }
343 byte_count += region->region_size;
344 // The image descriptor must be part of a static region.
345 if (d_offset >= region->region_offset && d_offset < byte_count)
346 {
347 d_region_num = i;
348 CPRINTS(ctx,
349 "validate_payload_regions: image descriptor in region %d\n",
350 i);
351 // The descriptor can't span regions.
352 if (ctx->descriptor.descriptor_area_size > byte_count ||
353 !(region->region_attributes & IMAGE_REGION_STATIC))
Nan Zhou7a337042021-07-26 21:05:21 -0700354 {
Patrick Williams60849572023-10-20 11:19:52 -0500355 CPRINTS(ctx,
356 "validate_payload_regions: descriptor must reside in "
357 "static region\n");
358 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
Nan Zhou7a337042021-07-26 21:05:21 -0700359 }
Nan Zhou7a337042021-07-26 21:05:21 -0700360 }
Patrick Williams60849572023-10-20 11:19:52 -0500361 }
362 if (byte_count != image_size)
363 {
364 CPRINTS(ctx, "validate_payload_regions: invalid image size\n");
365 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
366 }
367
368 rv = get_hash_digest_size(ctx->descriptor.hash_type, &digest_size);
369 if (rv != LIBCR51SIGN_SUCCESS)
370 {
Nan Zhou7a337042021-07-26 21:05:21 -0700371 return rv;
372 }
373
Patrick Williams60849572023-10-20 11:19:52 -0500374 rv = intf->read(ctx, hash_offset,
375 MEMBER_SIZE(struct hash_sha256, hash_magic) + digest_size,
376 magic_and_digest);
377 if (rv != LIBCR51SIGN_SUCCESS)
Nan Zhou7a337042021-07-26 21:05:21 -0700378 {
Patrick Williams60849572023-10-20 11:19:52 -0500379 CPRINTS(ctx,
William A. Kennington IIIaf46bea2021-12-18 14:35:45 -0800380 "validate_payload_regions: failed to read hash from flash\n");
Patrick Williams60849572023-10-20 11:19:52 -0500381 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
382 }
383 if (*(uint32_t*)magic_and_digest != HASH_MAGIC)
384 {
385 CPRINTS(ctx, "validate_payload_regions: bad hash magic\n");
386 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
387 }
388 rv = intf->hash_init(ctx, ctx->descriptor.hash_type);
389 if (rv != LIBCR51SIGN_SUCCESS)
390 {
391 CPRINTS(ctx, "validate_payload_regions: hash_init failed\n");
392 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
393 }
394 for (i = 0; i < region_count; i++)
395 {
396 uint32_t hash_start, hash_size;
397 region = image_regions->image_regions + i;
Willy Tudf800482021-09-17 22:06:18 -0700398
Patrick Williams60849572023-10-20 11:19:52 -0500399 if (!(region->region_attributes & IMAGE_REGION_STATIC))
400 {
401 continue;
402 }
403 hash_start = region->region_offset;
404 hash_size = region->region_size;
405
406 // Skip the descriptor.
407 do
408 {
409 if (i == d_region_num)
Nan Zhou7a337042021-07-26 21:05:21 -0700410 {
Patrick Williams60849572023-10-20 11:19:52 -0500411 hash_size = d_offset - hash_start;
Nan Zhou7a337042021-07-26 21:05:21 -0700412 }
Nan Zhou7a337042021-07-26 21:05:21 -0700413
Patrick Williams60849572023-10-20 11:19:52 -0500414 if (!hash_size)
Nan Zhou7a337042021-07-26 21:05:21 -0700415 {
Patrick Williams60849572023-10-20 11:19:52 -0500416 hash_start += ctx->descriptor.descriptor_area_size;
417 hash_size =
418 (region->region_offset + region->region_size - hash_start);
William A. Kennington IIIaf46bea2021-12-18 14:35:45 -0800419 }
Patrick Williams60849572023-10-20 11:19:52 -0500420 CPRINTS("validate_payload_regions: hashing %s (%x - %x)\n",
421 (const char*)region->region_name, hash_start,
422 hash_start + hash_size);
423 // Read the image_region array.
424 rv = read_and_hash_update(ctx, intf, hash_start, hash_size);
Nan Zhou7a337042021-07-26 21:05:21 -0700425 if (rv != LIBCR51SIGN_SUCCESS)
426 {
427 return rv;
428 }
Patrick Williams60849572023-10-20 11:19:52 -0500429 hash_start += hash_size;
430 } while (hash_start != region->region_offset + region->region_size);
431 }
432 rv = intf->hash_final((void*)ctx, (uint8_t*)dcrypto_digest);
433
434 if (rv != LIBCR51SIGN_SUCCESS)
435 {
436 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
Nan Zhou7a337042021-07-26 21:05:21 -0700437 }
438
Patrick Williams60849572023-10-20 11:19:52 -0500439 if (memcmp(magic_and_digest + MEMBER_SIZE(struct hash_sha256, hash_magic),
440 dcrypto_digest, digest_size))
Nan Zhou7a337042021-07-26 21:05:21 -0700441 {
Patrick Williams60849572023-10-20 11:19:52 -0500442 CPRINTS(ctx, "validate_payload_regions: invalid hash\n");
443 return LIBCR51SIGN_ERROR_INVALID_HASH;
444 }
445 // Image is valid.
446 return LIBCR51SIGN_SUCCESS;
447}
Nan Zhou7a337042021-07-26 21:05:21 -0700448
Patrick Williams60849572023-10-20 11:19:52 -0500449// Create empty image_regions to pass to validate_payload_regions
450// Support validate_payload_regions_helper to remove image_regions as a
451// required input.
452
453static failure_reason
454 allocate_and_validate_payload_regions(const struct libcr51sign_ctx* ctx,
455 struct libcr51sign_intf* intf,
456 uint32_t d_offset)
457{
458 struct libcr51sign_validated_regions image_regions;
459 return validate_payload_regions(ctx, intf, d_offset, &image_regions);
460}
461
462// Wrapper around validate_payload_regions to allow nullptr for
463// image_regions. Calls allocate_and_validate_payload_regions when
464// image_regions is nullptr to create placer holder image_regions.
465
466static failure_reason validate_payload_regions_helper(
467 const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
468 uint32_t d_offset, struct libcr51sign_validated_regions* image_regions)
469{
470 if (image_regions)
471 {
472 return validate_payload_regions(ctx, intf, d_offset, image_regions);
473 }
474
475 return allocate_and_validate_payload_regions(ctx, intf, d_offset);
476}
477
478// Check if the given signature_scheme is supported.
479// Returns nonzero on error, zero on success
480
481static failure_reason
482 is_signature_scheme_supported(enum signature_scheme scheme)
483{
484 switch (scheme)
485 {
486 case SIGNATURE_RSA2048_PKCS15:
487 case SIGNATURE_RSA3072_PKCS15:
488 case SIGNATURE_RSA4096_PKCS15:
489 case SIGNATURE_RSA4096_PKCS15_SHA512:
490 return LIBCR51SIGN_SUCCESS;
491 default:
492 return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
493 }
494}
495
496// Returns size of signature struct size in |size|
497// Returns nonzero on error, zero on success
498
499static failure_reason get_signature_struct_size(enum signature_scheme scheme,
500 uint32_t* size)
501{
502 switch (scheme)
503 {
504 case SIGNATURE_RSA2048_PKCS15:
505 *size = sizeof(struct signature_rsa2048_pkcs15);
506 return LIBCR51SIGN_SUCCESS;
507 case SIGNATURE_RSA3072_PKCS15:
508 *size = sizeof(struct signature_rsa3072_pkcs15);
509 return LIBCR51SIGN_SUCCESS;
510 case SIGNATURE_RSA4096_PKCS15:
511 case SIGNATURE_RSA4096_PKCS15_SHA512:
512 *size = sizeof(struct signature_rsa4096_pkcs15);
513 return LIBCR51SIGN_SUCCESS;
514 default:
515 return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
516 }
517}
518
519static failure_reason get_signature_field_offset(enum signature_scheme scheme,
520 uint32_t* offset)
521{
522 switch (scheme)
523 {
524 case SIGNATURE_RSA2048_PKCS15:
525 *offset = offsetof(struct signature_rsa2048_pkcs15, signature);
526 return LIBCR51SIGN_SUCCESS;
527 case SIGNATURE_RSA3072_PKCS15:
528 *offset = offsetof(struct signature_rsa3072_pkcs15, signature);
529 return LIBCR51SIGN_SUCCESS;
530 case SIGNATURE_RSA4096_PKCS15:
531 case SIGNATURE_RSA4096_PKCS15_SHA512:
532 *offset = offsetof(struct signature_rsa4096_pkcs15, signature);
533 return LIBCR51SIGN_SUCCESS;
534 default:
535 return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
536 }
537}
538
539// Validates the signature (of type scheme) read from "device" at
540//"raw_signature_offset" with "public_key" over a SHA256/SHA512 digest of
541// EEPROM area "data_offset:data_size".
542
543static failure_reason validate_signature(const struct libcr51sign_ctx* ctx,
544 const struct libcr51sign_intf* intf,
545 uint32_t data_offset,
546 uint32_t data_size,
547 enum signature_scheme scheme,
548 uint32_t raw_signature_offset)
549{
550 uint8_t signature[LIBCR51SIGN_MAX_SIGNATURE_SIZE];
551 uint16_t key_size;
552 uint32_t digest_size;
553 uint8_t dcrypto_digest[LIBCR51SIGN_SHA512_DIGEST_SIZE];
554 int rv;
555 enum hash_type hash_type;
556
557 if (!intf->hash_init)
558 {
559 CPRINTS(ctx, "validate_signature: missing hash_init\n");
560 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
561 }
562 rv = get_hash_type_from_signature(scheme, &hash_type);
563 if (rv != LIBCR51SIGN_SUCCESS)
564 {
565 CPRINTS(ctx,
566 "validate_payload_regions: hash_type from signature failed\n");
567 return rv;
568 }
569 rv = intf->hash_init(ctx, hash_type);
570 if (rv != LIBCR51SIGN_SUCCESS)
571 {
572 CPRINTS(ctx, "validate_payload_regions: hash_init failed\n");
573 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
574 }
575 rv = read_and_hash_update(ctx, intf, data_offset, data_size);
576 if (rv != LIBCR51SIGN_SUCCESS)
577 {
578 CPRINTS(ctx, "validate_signature: hash_update failed\n");
579 return rv;
580 }
581 if (!intf->hash_final)
582 {
583 CPRINTS(ctx, "validate_signature: missing hash_final\n");
584 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
585 }
586 rv = intf->hash_final((void*)ctx, dcrypto_digest);
587 if (rv != LIBCR51SIGN_SUCCESS)
588 {
589 CPRINTS(ctx, "validate_signature: hash_final failed (status = %d)\n",
590 rv);
591 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
592 }
593 rv = get_key_size(scheme, &key_size);
594 if (rv != LIBCR51SIGN_SUCCESS)
595 {
596 return rv;
597 }
598
599 rv = intf->read(ctx, raw_signature_offset, key_size, signature);
600 if (rv != LIBCR51SIGN_SUCCESS)
601 {
602 CPRINTS(ctx,
603 "validate_signature: failed to read signature (status = %d)\n",
604 rv);
605 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
606 }
607 if (!intf->verify_signature)
608 {
609 CPRINTS(ctx, "validate_signature: missing verify_signature\n");
610 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
611 }
612 rv = get_hash_digest_size(hash_type, &digest_size);
613 if (rv != LIBCR51SIGN_SUCCESS)
614 {
615 return rv;
616 }
617 rv = intf->verify_signature(ctx, scheme, signature, key_size,
618 dcrypto_digest, digest_size);
619 if (rv != LIBCR51SIGN_SUCCESS)
620 {
621 CPRINTS(ctx, "validate_signature: verification failed (status = %d)\n",
622 rv);
623 return LIBCR51SIGN_ERROR_INVALID_SIGNATURE;
624 }
625 CPRINTS(ctx, "validate_signature: verification succeeded\n");
626 return LIBCR51SIGN_SUCCESS;
627}
628
629// Sanity checks the image descriptor & validates its signature.
630// This function does not validate the image_region array or image hash.
631//
632//@param[in] ctx context which describes the image and holds opaque private
633// data for the user of the library
634//@param[in] intf function pointers which interface to the current system
635// and environment
636//@param offset Absolute image descriptor flash offset.
637//@param relative_offset Image descriptor offset relative to image start.
638//@param max_size Maximum size of the flash space in bytes.
639//@param descriptor Output pointer to an image_descriptor struct
640
641static failure_reason validate_descriptor(const struct libcr51sign_ctx* ctx,
642 const struct libcr51sign_intf* intf,
643 uint32_t offset,
644 uint32_t relative_offset,
645 uint32_t max_size)
646{
647 uint32_t max_descriptor_size, signed_size, signature_scheme,
648 signature_offset;
649 uint32_t signature_struct_offset, signature_struct_size, hash_struct_size;
650 int rv;
651
652 max_descriptor_size = max_size - relative_offset;
653 if (max_size < relative_offset ||
654 max_descriptor_size < sizeof(struct image_descriptor))
655 {
656 CPRINTS(ctx, "validate_descriptor: invalid arguments\n");
657 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
658 }
659
660 rv = intf->read(ctx, offset, sizeof(ctx->descriptor),
661 (uint8_t*)&ctx->descriptor);
662 if (rv != LIBCR51SIGN_SUCCESS)
663 {
664 CPRINTS(ctx, "validate_descriptor: failed to read descriptor\n");
665 return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
666 }
667 if (ctx->descriptor.descriptor_magic != DESCRIPTOR_MAGIC ||
668 ctx->descriptor.descriptor_offset != relative_offset ||
669 ctx->descriptor.region_count == 0 ||
670 ctx->descriptor.descriptor_area_size > max_descriptor_size ||
671 ctx->descriptor.image_size > max_size)
672 {
673 CPRINTS(ctx, "validate_descriptor: invalid descriptor\n");
674 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
675 }
676 if (intf->image_size_valid == NULL)
677 {
678 // Preserve original behavior of requiring exact image_size match if
679 // no operator is provided.
680 if (ctx->descriptor.image_size != max_size)
Nan Zhou7a337042021-07-26 21:05:21 -0700681 {
Patrick Williams60849572023-10-20 11:19:52 -0500682 CPRINTS(ctx, "validate_descriptor: invalid image size\n");
683 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
Nan Zhou7a337042021-07-26 21:05:21 -0700684 }
Patrick Williams60849572023-10-20 11:19:52 -0500685 }
686 else if (!intf->image_size_valid(ctx->descriptor.image_size))
687 {
688 CPRINTS(ctx, "validate_descriptor: invalid image size\n");
689 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
690 }
691 if (ctx->descriptor.image_type != IMAGE_DEV &&
692 ctx->descriptor.image_type != IMAGE_PROD &&
693 ctx->descriptor.image_type != IMAGE_BREAKOUT &&
694 ctx->descriptor.image_type != IMAGE_TEST &&
695 ctx->descriptor.image_type != IMAGE_UNSIGNED_INTEGRITY)
696 {
697 CPRINTS(ctx, "validate_descriptor: bad image type\n");
698 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
699 }
700 // Although the image_descriptor struct supports unauthenticated
701 // images, Haven will not allow it.
702 // Haven only supports SHA256 + RSA2048/RSA3072_PKCS15 currently.
703
704 signature_scheme = ctx->descriptor.signature_scheme;
705
706 rv = is_signature_scheme_supported(signature_scheme);
707 if (rv != LIBCR51SIGN_SUCCESS)
708 {
709 return rv;
710 }
711 rv = is_hash_type_supported(ctx->descriptor.hash_type);
712 if (rv != LIBCR51SIGN_SUCCESS)
713 {
714 CPRINTS(ctx, "validate_payload_regions: invalid hash type\n");
715 return rv;
716 }
717 if (ctx->descriptor.descriptor_major > MAX_MAJOR_VERSION ||
718 ctx->descriptor.region_count > LIBCR51SIGN_MAX_REGION_COUNT)
719 {
720 CPRINTS(ctx, "validate_descriptor: unsupported descriptor\n");
721 return LIBCR51SIGN_ERROR_UNSUPPORTED_DESCRIPTOR;
722 }
723 rv = get_signature_struct_size(signature_scheme, &signature_struct_size);
724 if (rv != LIBCR51SIGN_SUCCESS)
725 {
726 return rv;
727 }
728
729 // Compute the size of the signed portion of the image descriptor.
730 signed_size = sizeof(struct image_descriptor) +
731 ctx->descriptor.region_count * sizeof(struct image_region);
732 rv = get_hash_struct_size(ctx->descriptor.hash_type, &hash_struct_size);
733 if (rv != LIBCR51SIGN_SUCCESS)
734 {
735 return rv;
736 }
737 signed_size += hash_struct_size;
738 if (ctx->descriptor.denylist_size)
739 {
740 signed_size += sizeof(struct denylist);
741 signed_size += ctx->descriptor.denylist_size *
742 sizeof(struct denylist_record);
743 }
744 if (ctx->descriptor.blob_size)
745 {
746 signed_size += sizeof(struct blob);
747 // Previous additions are guaranteed not to overflow.
748 if (ctx->descriptor.blob_size >
749 ctx->descriptor.descriptor_area_size - signed_size)
Nan Zhou7a337042021-07-26 21:05:21 -0700750 {
Patrick Williams60849572023-10-20 11:19:52 -0500751 CPRINTS(ctx, "validate_descriptor: invalid blob size (0x%x)\n",
752 ctx->descriptor.blob_size);
753 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
Nan Zhou7a337042021-07-26 21:05:21 -0700754 }
Patrick Williams60849572023-10-20 11:19:52 -0500755 signed_size += ctx->descriptor.blob_size;
756 }
757 if (signature_struct_size >
758 ctx->descriptor.descriptor_area_size - signed_size)
759 {
760 CPRINTS(ctx,
761 "validate_descriptor: invalid descriptor area size "
762 "(expected = 0x%x, actual = 0x%x)\n",
763 ctx->descriptor.descriptor_area_size,
764 signed_size + signature_struct_size);
765 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
766 }
767 signature_struct_offset = signed_size;
768 // Omit the actual signature.
769 rv = get_signature_field_offset(signature_scheme, &signature_offset);
770 if (rv != LIBCR51SIGN_SUCCESS)
771 {
772 return rv;
773 }
774 signed_size += signature_offset;
Nan Zhou7a337042021-07-26 21:05:21 -0700775
Patrick Williams60849572023-10-20 11:19:52 -0500776 // Lookup key & validate transition.
777 rv = validate_transition(ctx, intf, offset + signature_struct_offset);
778
779 if (rv != LIBCR51SIGN_SUCCESS)
780 {
781 return rv;
782 }
783 return validate_signature(ctx, intf, offset, signed_size, signature_scheme,
784 offset + signed_size);
785}
786
787// Scans the external EEPROM for a magic value at "alignment" boundaries.
788//
789//@param device Handle to the external EEPROM.
790//@param magic 8-byte pattern to search for.
791//@param start_offset Offset to begin searching at.
792//@param limit Exclusive address (e.g. EEPROM size).
793//@param alignment Alignment boundaries (POW2) to search on.
794//@param header_offset Location to place the new header offset.
795//@return LIBCR51SIGN_SUCCESS (or non-zero on error).
796
797int scan_for_magic_8(const struct libcr51sign_ctx* ctx,
798 const struct libcr51sign_intf* intf, uint64_t magic,
799 uint32_t start_offset, uint32_t limit, uint32_t alignment,
800 uint32_t* header_offset)
801{
802 uint64_t read_data;
803 uint32_t offset;
804 int rv;
805
806 if (limit <= start_offset || limit > ctx->end_offset ||
807 limit < sizeof(magic) || !POWER_OF_TWO(alignment))
808 {
809 return LIBCR51SIGN_ERROR_INVALID_ARGUMENT;
810 }
811
812 if (!intf->read)
813 {
814 CPRINTS(ctx, "scan_for_magic_8: missing intf->read\n");
815 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
816 }
817 // Align start_offset to the next valid boundary.
818 start_offset = ((start_offset - 1) & ~(alignment - 1)) + alignment;
819 for (offset = start_offset; offset < limit - sizeof(magic);
820 offset += alignment)
821 {
822 rv = intf->read((void*)ctx, offset, sizeof(read_data),
823 (uint8_t*)&read_data);
824 if (rv != LIBCR51SIGN_SUCCESS)
Nan Zhou7a337042021-07-26 21:05:21 -0700825 {
Patrick Williams60849572023-10-20 11:19:52 -0500826 return rv;
827 }
828 if (read_data == magic)
829 {
830 if (header_offset)
Nan Zhou7a337042021-07-26 21:05:21 -0700831 {
Patrick Williams60849572023-10-20 11:19:52 -0500832 *header_offset = offset;
Nan Zhou7a337042021-07-26 21:05:21 -0700833 }
Patrick Williams60849572023-10-20 11:19:52 -0500834 return LIBCR51SIGN_SUCCESS;
835 }
836 }
837 // Failed to locate magic.
838 return LIBCR51SIGN_ERROR_FAILED_TO_LOCATE_MAGIC;
839}
Nan Zhou7a337042021-07-26 21:05:21 -0700840
Patrick Williams60849572023-10-20 11:19:52 -0500841// Check whether the signature on the image is valid.
842// Validates the authenticity of an EEPROM image. Scans for & validates the
843// signature on the image descriptor. If the descriptor validates, hashes
844// the rest of the image to verify its integrity.
845//
846// @param[in] ctx - context which describes the image and holds opaque
847// private
848// data for the user of the library
849// @param[in] intf - function pointers which interface to the current system
850// and environment
851// @param[out] image_regions - image_region pointer to an array for the
852// output
853//
854// @return nonzero on error, zero on success
855
856failure_reason
857 libcr51sign_validate(const struct libcr51sign_ctx* ctx,
858 struct libcr51sign_intf* intf,
859 struct libcr51sign_validated_regions* image_regions)
860{
861 uint32_t image_limit = 0;
862 int rv, rv_first_desc = LIBCR51SIGN_SUCCESS;
863 uint32_t descriptor_offset;
864
865 if (!ctx)
866 {
867 CPRINTS(ctx, "Missing context\n");
868 return LIBCR51SIGN_ERROR_INVALID_CONTEXT;
869 }
870 else if (!intf)
871 {
872 CPRINTS(ctx, "Missing interface\n");
873 return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
874 }
875
876 rv = scan_for_magic_8(ctx, intf, DESCRIPTOR_MAGIC, ctx->start_offset,
877 ctx->end_offset, DESCRIPTOR_ALIGNMENT,
878 &descriptor_offset);
879 while (rv == LIBCR51SIGN_SUCCESS)
880 {
881 CPRINTS(ctx, "validate: potential image descriptor found @%x\n",
882 descriptor_offset);
883 // Validation is split into 2 functions to minimize
884 // stack usage.
885
886 rv = validate_descriptor(ctx, intf, descriptor_offset,
887 descriptor_offset - ctx->start_offset,
888 ctx->end_offset - ctx->start_offset);
889 if (rv != LIBCR51SIGN_SUCCESS)
890 {
891 CPRINTS(ctx, "validate: validate_descriptor() failed ec%d\n", rv);
892 }
893
894 if (rv == LIBCR51SIGN_SUCCESS)
895 {
896 rv = validate_payload_regions_helper(ctx, intf, descriptor_offset,
897 image_regions);
Nan Zhou7a337042021-07-26 21:05:21 -0700898 if (rv == LIBCR51SIGN_SUCCESS)
899 {
Patrick Williams60849572023-10-20 11:19:52 -0500900 CPRINTS(ctx, "validate: success!\n");
901 return rv;
Nan Zhou7a337042021-07-26 21:05:21 -0700902 }
Patrick Williams60849572023-10-20 11:19:52 -0500903 CPRINTS(ctx, "validate: validate_payload_regions() failed ec%d\n",
904 rv);
Nan Zhou7a337042021-07-26 21:05:21 -0700905 }
Patrick Williams60849572023-10-20 11:19:52 -0500906 // Store the first desc fail reason if any
907 if (rv != LIBCR51SIGN_SUCCESS && rv_first_desc == LIBCR51SIGN_SUCCESS)
908 rv_first_desc = rv;
909
910 // scan_for_magic_8() will round up to the next aligned boundary.
911 descriptor_offset++;
912 image_limit = ctx->end_offset - ctx->start_offset;
913 rv = scan_for_magic_8(ctx, intf, DESCRIPTOR_MAGIC, descriptor_offset,
914 image_limit, DESCRIPTOR_ALIGNMENT,
915 &descriptor_offset);
Nan Zhou7a337042021-07-26 21:05:21 -0700916 }
Patrick Williams60849572023-10-20 11:19:52 -0500917 CPRINTS(ctx, "validate: failed to validate image ec%d\n", rv);
918 // If desc validation failed for some reason then return that reason
919 if (rv_first_desc != LIBCR51SIGN_SUCCESS)
920 return rv_first_desc;
921 else
922 return rv;
923}
Nan Zhou7a337042021-07-26 21:05:21 -0700924
Patrick Williams60849572023-10-20 11:19:52 -0500925// @func to returns the libcr51sign error code as a string
926// @param[in] ec - Error code
927// @return error code in string format
Nan Zhou7a337042021-07-26 21:05:21 -0700928
Patrick Williams60849572023-10-20 11:19:52 -0500929const char* libcr51sign_errorcode_to_string(failure_reason ec)
930{
931 switch (ec)
Nan Zhou7a337042021-07-26 21:05:21 -0700932 {
Patrick Williams60849572023-10-20 11:19:52 -0500933 case LIBCR51SIGN_SUCCESS:
934 return "Success";
935 case LIBCR51SIGN_ERROR_RUNTIME_FAILURE:
936 return "Runtime Error Failure";
937 case LIBCR51SIGN_ERROR_UNSUPPORTED_DESCRIPTOR:
938 return "Unsupported descriptor";
939 case LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR:
940 return "Invalid descriptor";
941 case LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY:
942 return "Invalid image family";
943 case LIBCR51SIGN_ERROR_IMAGE_TYPE_DISALLOWED:
944 return "Image type disallowed";
945 case LIBCR51SIGN_ERROR_DEV_DOWNGRADE_DISALLOWED:
946 return "Dev downgrade disallowed";
947 case LIBCR51SIGN_ERROR_UNTRUSTED_KEY:
948 return "Untrusted key";
949 case LIBCR51SIGN_ERROR_INVALID_SIGNATURE:
950 return "Invalid signature";
951 case LIBCR51SIGN_ERROR_INVALID_HASH:
952 return "Invalid hash";
953 case LIBCR51SIGN_ERROR_INVALID_HASH_TYPE:
954 return "Invalid hash type";
955 case LIBCR51SIGN_ERROR_INVALID_ARGUMENT:
956 return "Invalid Argument";
957 case LIBCR51SIGN_ERROR_FAILED_TO_LOCATE_MAGIC:
958 return "Failed to locate descriptor";
959 case LIBCR51SIGN_ERROR_INVALID_CONTEXT:
960 return "Invalid context";
961 case LIBCR51SIGN_ERROR_INVALID_INTERFACE:
962 return "Invalid interface";
963 case LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME:
964 return "Invalid signature scheme";
965 case LIBCR51SIGN_ERROR_INVALID_REGION_INPUT:
966 return "Invalid image region input";
967 case LIBCR51SIGN_ERROR_INVALID_REGION_SIZE:
968 return "Invalid image region size";
969 default:
970 return "Unknown error";
Nan Zhou7a337042021-07-26 21:05:21 -0700971 }
Patrick Williams60849572023-10-20 11:19:52 -0500972}
Nan Zhou7a337042021-07-26 21:05:21 -0700973
974#ifdef __cplusplus
975} // extern "C"
976#endif