google-misc: libcr51sign: add feature to fetch image regions

Change-Id: I6bd45b0f855ac7c035b294e461bbe9821fb3895b
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/subprojects/libcr51sign/cr51_image_descriptor.h b/subprojects/libcr51sign/cr51_image_descriptor.h
index 1341ba9..0f4bf6f 100644
--- a/subprojects/libcr51sign/cr51_image_descriptor.h
+++ b/subprojects/libcr51sign/cr51_image_descriptor.h
@@ -56,6 +56,10 @@
 #define IMAGE_REGION_PERSISTENT (1 << 4)
 #define IMAGE_REGION_PERSISTENT_RELOCATABLE (1 << 5)
 #define IMAGE_REGION_PERSISTENT_EXPANDABLE (1 << 6)
+#define IMAGE_REGION_OVERRIDE (1 << 7)
+#define IMAGE_REGION_OVERRIDE_ON_TRANSITION (1 << 8)
+#define IMAGE_REGION_MAILBOX (1 << 9)
+#define IMAGE_REGION_SKIP_BOOT_VALIDATION (1 << 10)
 
 /* Little endian on flash. */
 #define DESCRIPTOR_MAGIC 0x5f435344474d495f // "_IMGDSC_"
@@ -68,11 +72,12 @@
 /* Indicates the type of the image. The type of the image also indicates the
  * family of key that was used to sign the image.
  *
- * Note: if the image type is IMAGE_SELF, the signature_scheme has to be of type
+ * Note: if the image type is IMAGE_UNSIGNED_INTEGRITY, the signature_scheme has
+ * to be of type
  * *_NO_SIGNATURE. Also, all other image types cannot transition to an image of
- * type IMAGE_SELF.
+ * type IMAGE_UNSIGNED_INTEGRITY.
  *
- * The way to verify an image of type IMAGE_SELF differs from
+ * The way to verify an image of type IMAGE_UNSIGNED_INTEGRITY differs from
  * other types of images as it is not signed with an asymmetric key. Instead,
  * one can verify the integrity by computing the shasum over the descriptor.
  */
@@ -82,7 +87,7 @@
     IMAGE_PROD = 1,
     IMAGE_BREAKOUT = 2,
     IMAGE_TEST = 3,
-    IMAGE_SELF = 4
+    IMAGE_UNSIGNED_INTEGRITY = 4
 };
 
 enum hash_type
@@ -98,8 +103,8 @@
     HASH_SHA3_512 = 8
 };
 
-/* Note: If the image is of type IMAGE_SELF, the signature_scheme has to be of
- * type *_ONLY_NO_SIGNATURE.
+/* Note: If the image is of type IMAGE_UNSIGNED_INTEGRITY, the signature_scheme
+ * has to be of type *_ONLY_NO_SIGNATURE.
  */
 enum signature_scheme
 {
@@ -196,7 +201,7 @@
     /* Seconds since epoch. */
     uint64_t build_timestamp;
 
-    /* image_type enum { DEV, PROD, BREAKOUT, SELF} */
+    /* image_type enum { DEV, PROD, BREAKOUT, UNSIGNED_INTEGRITY} */
     uint8_t image_type;
     /* 0: no denylist struct, 1: watermark only, >1: watermark + denylist */
     uint8_t denylist_size;
diff --git a/subprojects/libcr51sign/libcr51sign.c b/subprojects/libcr51sign/libcr51sign.c
index 5546f86..7d2e1bb 100644
--- a/subprojects/libcr51sign/libcr51sign.c
+++ b/subprojects/libcr51sign/libcr51sign.c
@@ -37,8 +37,6 @@
 // Maximum version supported. Major revisions are not backwards compatible.
 #define MAX_MAJOR_VERSION 1
 
-#define MAX_REGION_COUNT 16
-
 // Descriptor alignment on the external EEPROM.
 #define DESCRIPTOR_ALIGNMENT (64 * 1024)
 
@@ -125,10 +123,10 @@
         switch (type)
         {
             case HASH_SHA2_256:
-                *size = SHA256_DIGEST_SIZE;
+                *size = LIBCR51SIGN_SHA256_DIGEST_SIZE;
                 return LIBCR51SIGN_SUCCESS;
             case HASH_SHA2_512:
-                *size = SHA512_DIGEST_SIZE;
+                *size = LIBCR51SIGN_SHA512_DIGEST_SIZE;
                 return LIBCR51SIGN_SUCCESS;
             default:
                 return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
@@ -183,7 +181,10 @@
                           SIGNATURE_OFFSET));
 
         // Read up to the modulus.
-        const uint32_t read_len = SIGNATURE_OFFSET;
+        enum
+        {
+            read_len = SIGNATURE_OFFSET
+        };
         uint8_t buffer[read_len];
         // "modulus" & "signature" will not be indexed.
         struct signature_rsa4096_pkcs15* sig_data = (void*)&buffer;
@@ -282,19 +283,24 @@
     // If the array is consistent, proceeds to hash the static regions and
     // validates the hash. d_offset is the absolute image descriptor offset
 
-    static failure_reason
-        validate_payload_regions(const struct libcr51sign_ctx* ctx,
-                                 struct libcr51sign_intf* intf,
-                                 uint32_t d_offset)
+    static failure_reason validate_payload_regions(
+        const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
+        uint32_t d_offset, struct libcr51sign_validated_regions* image_regions)
     {
         // Allocate buffer to accomodate largest supported hash-type(SHA512)
         uint8_t magic_and_digest[MEMBER_SIZE(struct hash_sha512, hash_magic) +
-                                 SHA512_DIGEST_SIZE];
-        uint8_t dcrypto_digest[SHA512_DIGEST_SIZE];
-        struct image_region regions[MAX_REGION_COUNT];
+                                 LIBCR51SIGN_SHA512_DIGEST_SIZE];
+        uint8_t dcrypto_digest[LIBCR51SIGN_SHA512_DIGEST_SIZE];
         uint32_t byte_count, region_count, image_size, hash_offset, digest_size;
         uint8_t d_region_num = 0;
         int i, rv;
+        struct image_region const* region;
+
+        if (image_regions == NULL)
+        {
+            CPRINTS(ctx, "Missing image region input");
+            return LIBCR51SIGN_ERROR_INVALID_REGION_INPUT;
+        }
 
         BUILD_ASSERT((MEMBER_SIZE(struct hash_sha256, hash_magic) ==
                       MEMBER_SIZE(struct hash_sha512, hash_magic)));
@@ -303,9 +309,22 @@
         hash_offset = d_offset + sizeof(struct image_descriptor) +
                       region_count * sizeof(struct image_region);
         // Read the image_region array.
+
+        if (region_count > ARRAY_SIZE(image_regions->image_regions))
+        {
+            CPRINTS(ctx, "validate_payload_regions: "
+                         "ctx->descriptor.region_count is greater "
+                         "than LIBCR51SIGN_MAX_REGION_COUNT");
+            return LIBCR51SIGN_ERROR_INVALID_REGION_SIZE;
+        }
+
         rv = intf->read(
             ctx, d_offset + offsetof(struct image_descriptor, image_regions),
-            region_count * sizeof(struct image_region), (uint8_t*)&regions);
+            region_count * sizeof(struct image_region),
+            (uint8_t*)&image_regions->image_regions);
+
+        image_regions->region_count = region_count;
+
         if (rv != LIBCR51SIGN_SUCCESS)
         {
             CPRINTS(ctx,
@@ -316,27 +335,29 @@
         // Validate that the regions are contiguous & exhaustive.
         for (i = 0, byte_count = 0; i < region_count; i++)
         {
+            region = image_regions->image_regions + i;
+
             CPRINTS(ctx,
                     "validate_payload_regions: region #%d \"%s\" (%x - %x)", i,
-                    regions[i].region_name, regions[i].region_offset,
-                    regions[i].region_offset + regions[i].region_size);
-            if ((regions[i].region_offset % IMAGE_REGION_ALIGNMENT) != 0 ||
-                (regions[i].region_size % IMAGE_REGION_ALIGNMENT) != 0)
+                    region->region_name, region->region_offset,
+                    region->region_offset + region->region_size);
+            if ((region->region_offset % IMAGE_REGION_ALIGNMENT) != 0 ||
+                (region->region_size % IMAGE_REGION_ALIGNMENT) != 0)
             {
                 CPRINTS(
                     ctx,
                     "validate_payload_regions: regions must be sector aligned");
                 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
             }
-            if (regions[i].region_offset != byte_count ||
-                regions[i].region_size > image_size - byte_count)
+            if (region->region_offset != byte_count ||
+                region->region_size > image_size - byte_count)
             {
                 CPRINTS(ctx, "validate_payload_regions: invalid region array");
                 return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
             }
-            byte_count += regions[i].region_size;
+            byte_count += region->region_size;
             // The image descriptor must be part of a static region.
-            if (d_offset >= regions[i].region_offset && d_offset < byte_count)
+            if (d_offset >= region->region_offset && d_offset < byte_count)
             {
                 d_region_num = i;
                 CPRINTS(
@@ -345,7 +366,7 @@
                     i);
                 // The descriptor can't span regions.
                 if (ctx->descriptor.descriptor_area_size > byte_count ||
-                    !(regions[i].region_attributes & IMAGE_REGION_STATIC))
+                    !(region->region_attributes & IMAGE_REGION_STATIC))
                 {
                     CPRINTS(
                         ctx,
@@ -391,12 +412,14 @@
         for (i = 0; i < region_count; i++)
         {
             uint32_t hash_start, hash_size;
-            if (!(regions[i].region_attributes & IMAGE_REGION_STATIC))
+            region = image_regions->image_regions + i;
+
+            if (!(region->region_attributes & IMAGE_REGION_STATIC))
             {
                 continue;
             }
-            hash_start = regions[i].region_offset;
-            hash_size = regions[i].region_size;
+            hash_start = region->region_offset;
+            hash_size = region->region_size;
 
             // Skip the descriptor.
             do
@@ -409,11 +432,11 @@
                 if (!hash_size)
                 {
                     hash_start += ctx->descriptor.descriptor_area_size;
-                    hash_size = (regions[i].region_offset +
-                                 regions[i].region_size - hash_start);
+                    hash_size = (region->region_offset + region->region_size -
+                                 hash_start);
                 }
                 CPRINTS("validate_payload_regions: hashing %s (%x - %x)",
-                        regions[i].region_name, hash_start,
+                        region->region_name, hash_start,
                         hash_start + hash_size);
                 // Read the image_region array.
                 rv = read_and_hash_update(ctx, intf, hash_start, hash_size);
@@ -422,8 +445,7 @@
                     return rv;
                 }
                 hash_start += hash_size;
-            } while (hash_start !=
-                     regions[i].region_offset + regions[i].region_size);
+            } while (hash_start != region->region_offset + region->region_size);
         }
         rv = intf->hash_final((void*)ctx, (uint8_t*)dcrypto_digest);
 
@@ -443,6 +465,35 @@
         return LIBCR51SIGN_SUCCESS;
     }
 
+    // Create empty image_regions to pass to validate_payload_regions
+    // Support validate_payload_regions_helper to remove image_regions as a
+    // required input.
+
+    static failure_reason
+        allocate_and_validate_payload_regions(const struct libcr51sign_ctx* ctx,
+                                              struct libcr51sign_intf* intf,
+                                              uint32_t d_offset)
+    {
+        struct libcr51sign_validated_regions image_regions;
+        return validate_payload_regions(ctx, intf, d_offset, &image_regions);
+    }
+
+    // Wrapper around validate_payload_regions to allow nullptr for
+    // image_regions. Calls allocate_and_validate_payload_regions when
+    // image_regions is nullptr to create placer holder image_regions.
+
+    static failure_reason validate_payload_regions_helper(
+        const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
+        uint32_t d_offset, struct libcr51sign_validated_regions* image_regions)
+    {
+        if (image_regions)
+        {
+            return validate_payload_regions(ctx, intf, d_offset, image_regions);
+        }
+
+        return allocate_and_validate_payload_regions(ctx, intf, d_offset);
+    }
+
     // Check if the given signature_scheme is supported.
     // Returns nonzero on error, zero on success
 
@@ -514,10 +565,10 @@
         uint32_t data_offset, uint32_t data_size, enum signature_scheme scheme,
         uint32_t raw_signature_offset)
     {
-        uint8_t signature[MAX_SIGNATURE_SIZE];
+        uint8_t signature[LIBCR51SIGN_MAX_SIGNATURE_SIZE];
         uint16_t key_size;
         uint32_t digest_size;
-        uint8_t dcrypto_digest[SHA512_DIGEST_SIZE];
+        uint8_t dcrypto_digest[LIBCR51SIGN_SHA512_DIGEST_SIZE];
         int rv;
         enum hash_type hash_type;
 
@@ -646,7 +697,7 @@
             ctx->descriptor.image_type != IMAGE_PROD &&
             ctx->descriptor.image_type != IMAGE_BREAKOUT &&
             ctx->descriptor.image_type != IMAGE_TEST &&
-            ctx->descriptor.image_type != IMAGE_SELF)
+            ctx->descriptor.image_type != IMAGE_UNSIGNED_INTEGRITY)
         {
             CPRINTS(ctx, "validate_descriptor: bad image type");
             return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
@@ -669,7 +720,7 @@
             return rv;
         }
         if (ctx->descriptor.descriptor_major > MAX_MAJOR_VERSION ||
-            ctx->descriptor.region_count > MAX_REGION_COUNT)
+            ctx->descriptor.region_count > LIBCR51SIGN_MAX_REGION_COUNT)
         {
             CPRINTS(ctx, "validate_descriptor: unsupported descriptor");
             return LIBCR51SIGN_ERROR_UNSUPPORTED_DESCRIPTOR;
@@ -804,11 +855,14 @@
     //                 data for the user of the library
     // @param[in] intf - function pointers which interface to the current system
     //                  and environment
+    // @param[out] image_regions - image_region pointer to an array for the
+    // output
     //
     // @return nonzero on error, zero on success
 
-    failure_reason libcr51sign_validate(const struct libcr51sign_ctx* ctx,
-                                        struct libcr51sign_intf* intf)
+    failure_reason libcr51sign_validate(
+        const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
+        struct libcr51sign_validated_regions* image_regions)
     {
         uint32_t image_limit = 0;
         int rv, rv_first_desc = LIBCR51SIGN_SUCCESS;
@@ -846,7 +900,8 @@
 
             if (rv == LIBCR51SIGN_SUCCESS)
             {
-                rv = validate_payload_regions(ctx, intf, descriptor_offset);
+                rv = validate_payload_regions_helper(
+                    ctx, intf, descriptor_offset, image_regions);
                 if (rv == LIBCR51SIGN_SUCCESS)
                 {
                     CPRINTS(ctx, "validate: success!");
@@ -916,6 +971,10 @@
                 return "Invalid interface";
             case LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME:
                 return "Invalid signature scheme";
+            case LIBCR51SIGN_ERROR_INVALID_REGION_INPUT:
+                return "Invalid image region input";
+            case LIBCR51SIGN_ERROR_INVALID_REGION_SIZE:
+                return "Invalid image region size";
             default:
                 return "Unknown error";
         }
diff --git a/subprojects/libcr51sign/libcr51sign.h b/subprojects/libcr51sign/libcr51sign.h
index 7210d26..d56fc05 100644
--- a/subprojects/libcr51sign/libcr51sign.h
+++ b/subprojects/libcr51sign/libcr51sign.h
@@ -26,11 +26,13 @@
 {
 #endif
 
-#define SHA256_DIGEST_SIZE 32
-#define SHA512_DIGEST_SIZE 64
+#define LIBCR51SIGN_SHA256_DIGEST_SIZE 32
+#define LIBCR51SIGN_SHA512_DIGEST_SIZE 64
+
+#define LIBCR51SIGN_MAX_REGION_COUNT 16
 
 // Currently RSA4096 (in bytes).
-#define MAX_SIGNATURE_SIZE 512
+#define LIBCR51SIGN_MAX_SIGNATURE_SIZE 512
 
     // List of common error codes that can be returned
     enum libcr51sign_validation_failure_reason
@@ -59,6 +61,9 @@
         LIBCR51SIGN_ERROR_INVALID_INTERFACE = 14,
         LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME = 15,
         LIBCR51SIGN_ERROR_MAX = 16,
+        // Invalid image region
+        LIBCR51SIGN_ERROR_INVALID_REGION_INPUT = 17,
+        LIBCR51SIGN_ERROR_INVALID_REGION_SIZE = 18,
     };
 
     struct libcr51sign_ctx
@@ -164,6 +169,12 @@
         bool (*is_production_mode)();
     };
 
+    struct libcr51sign_validated_regions
+    {
+        uint32_t region_count;
+        struct image_region image_regions[LIBCR51SIGN_MAX_REGION_COUNT];
+    };
+
     // Check whether the signature on the image is valid.
     // Validates the authenticity of an EEPROM image. Scans for & validates the
     // signature on the image descriptor. If the descriptor validates, hashes
@@ -174,12 +185,14 @@
     //                  data for the user of the library
     // @param[in] intf - function pointers which interface to the current system
     //                   and environment
+    // @param[out] image_regions - image_region pointer to an array for the
+    // output
     //
     // @return nonzero on error, zero on success
 
-    enum libcr51sign_validation_failure_reason
-        libcr51sign_validate(const struct libcr51sign_ctx* ctx,
-                             struct libcr51sign_intf* intf);
+    enum libcr51sign_validation_failure_reason libcr51sign_validate(
+        const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
+        struct libcr51sign_validated_regions* image_regions);
 
     // Function to convert error code to string format
     // @param[in] ec - error code