libcr51sign: import from gBMC

Validate the cr51 descriptor of the BIOS image. It can also
parse the BIOS version and write it to a file.

Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I098be66b228da6f3514d3f13166a1bb4e1e718fb
diff --git a/subprojects/libcr51sign/MAINTAINERS b/subprojects/libcr51sign/MAINTAINERS
new file mode 100644
index 0000000..a8f2c52
--- /dev/null
+++ b/subprojects/libcr51sign/MAINTAINERS
@@ -0,0 +1,45 @@
+How to use this list:
+    Find the most specific section entry (described below) that matches where
+    your change lives and add the reviewers (R) and maintainers (M) as
+    reviewers. You can use the same method to track down who knows a particular
+    code base best.
+
+    Your change/query may span multiple entries; that is okay.
+
+    If you do not find an entry that describes your request at all, someone
+    forgot to update this list; please at least file an issue or send an email
+    to a maintainer, but preferably you should just update this document.
+
+Description of section entries:
+
+    Section entries are structured according to the following scheme:
+
+    X:  NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>
+    X:  ...
+    .
+    .
+    .
+
+    Where REPO_NAME is the name of the repository within the OpenBMC GitHub
+    organization; FILE_PATH is a file path within the repository, possibly with
+    wildcards; X is a tag of one of the following types:
+
+    M:  Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+        if omitted from an entry, assume one of the maintainers from the
+        MAINTAINERS entry.
+    R:  Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+        these people are to be added as reviewers for a change matching the repo
+        path.
+    F:  Denotes forked from an external repository; has fields URL.
+
+    Line comments are to be denoted "# SOME COMMENT" (typical shell style
+    comment); it is important to follow the correct syntax and semantics as we
+    may want to use automated tools with this file in the future.
+
+    A change cannot be added to an OpenBMC repository without a MAINTAINER's
+    approval; thus, a MAINTAINER should always be listed as a reviewer.
+
+START OF MAINTAINERS LIST
+-------------------------
+
+M:  Nan Zhou <nanzhoumails@gmails.com> <nanzhou!>
diff --git a/subprojects/libcr51sign/OWNERS b/subprojects/libcr51sign/OWNERS
new file mode 100644
index 0000000..1dca102
--- /dev/null
+++ b/subprojects/libcr51sign/OWNERS
@@ -0,0 +1 @@
+nanzhoumails@gmail.com
diff --git a/subprojects/libcr51sign/README.md b/subprojects/libcr51sign/README.md
new file mode 100644
index 0000000..89496e8
--- /dev/null
+++ b/subprojects/libcr51sign/README.md
@@ -0,0 +1,44 @@
+## Cr51 Image Signature Library
+
+### Package `libcr51sign`
+
+*   Status: **Ready**
+
+Libcr51sign is a library to verify images signed in the Cr51 format which can be
+shared between all systems requiring this functionality. Given an absolute start
+and end offset the library would scan for and validate the signature on the
+image descriptor, if the image validates, hashes the rest of the image to verify
+its integrity. Because this library will be used across many varied platforms,
+it does not assume the presence of any standard libraries or operating system
+interfaces. In order to handle this, a struct containing function pointers that
+implement each piece of platform-specific functionality will be passed to the
+library’s functions. Interface struct should typically be static data (could put
+in rodata) while the data in context is mutable.
+
+### Debug
+
+Print will be handled via Macros. The user can define USER_PRINT or the library
+would use its default. The library will not assert on any error conditions,but
+will return error codes and expects the client to handle as deemed fit.
+
+```
+
+#ifndef USER_PRINT
+#define CPRINTS(ctx, format, args...)printf(format, ##args)
+#endif
+```
+
+### Prod/Dev transitions
+
+Prod --> Prod: Allowed \
+Prod --> Dev: Only if allowlisted/prod_to_dev_downgrade_allowed \
+Dev --> Prod: Allowed \
+Dev --> Dev: Allowed
+
+verify_signature: The implementation should check if the signature size passed
+is same as the length of the key
+
+Note: libcr51sign will also provide a companion library with default SW
+implementations of common functions like hash_init/final,
+read_and_hash_update().
+
diff --git a/subprojects/libcr51sign/cr51_image_descriptor.h b/subprojects/libcr51sign/cr51_image_descriptor.h
new file mode 100644
index 0000000..1341ba9
--- /dev/null
+++ b/subprojects/libcr51sign/cr51_image_descriptor.h
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef PLATFORMS_SECURITY_TITAN_CR51_IMAGE_DESCRIPTOR_H_
+#define PLATFORMS_SECURITY_TITAN_CR51_IMAGE_DESCRIPTOR_H_
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+/* _Static_assert is usually not enabled in C++ mode by compilers. */
+#include <assert.h>
+#define _Static_assert static_assert
+#endif
+/* This structure encodes a superset of what we have historically encoded in:
+ *
+ *  Unless explicitly noted all fields are little-endian & offset/size fields
+ *  are in bytes. This struct must reside in a IMAGE_REGION_STATIC region and
+ *  must also reside on a 64K boundary. The size of the hashed/signed portion
+ *  of the descriptor region can be determined solely by parsing the (fixed)
+ *  image_descriptor struct.
+ *
+ *  --------------------------------Flash layout--------------------------------
+ *  |                     struct image_descriptor (signed)                     |
+ *  |                struct image_region[region_count] (signed)                |
+ *  ----------------------------------------------------------------------------
+ *  |               (optional: hash_type) struct hash_* (signed)               |
+ *  ----------------------------------------------------------------------------
+ *  |           (optional: denylist_size) struct denylist (signed)             |
+ *  |             struct denylist_record[denylist_size] (signed)               |
+ *  ----------------------------------------------------------------------------
+ *  |                (optional: blob_size) struct blob (signed)                |
+ *  |                     uint8_t blob[blob_size] (signed)                     |
+ *  ----------------------------------------------------------------------------
+ *  |    (optional: signature_scheme) struct signature_* (partially signed)    |
+ *  ----------------------------------------------------------------------------
+ *  |           (optional) struct key_rotation_records (not signed)            |
+ *  ----------------------------------------------------------------------------
+ */
+
+#define IMAGE_REGION_STATIC (1 << 0)
+#define IMAGE_REGION_COMPRESSED (1 << 1)
+#define IMAGE_REGION_WRITE_PROTECTED (1 << 2)
+#define IMAGE_REGION_READ_PROTECTED (1 << 3)
+#define IMAGE_REGION_PERSISTENT (1 << 4)
+#define IMAGE_REGION_PERSISTENT_RELOCATABLE (1 << 5)
+#define IMAGE_REGION_PERSISTENT_EXPANDABLE (1 << 6)
+
+/* Little endian on flash. */
+#define DESCRIPTOR_MAGIC 0x5f435344474d495f // "_IMGDSC_"
+#define HASH_MAGIC 0x48534148               // "HASH"
+#define DENYLIST_MAGIC 0x4b434c42           // "BLCK"
+#define BLOB_MAGIC 0x424f4c42               // "BLOB"
+#define SIGNATURE_MAGIC 0x4e474953          // "SIGN"
+#define ROTATION_MAGIC 0x5254524b           // "KRTR"
+
+/* 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
+ * *_NO_SIGNATURE. Also, all other image types cannot transition to an image of
+ * type IMAGE_SELF.
+ *
+ * The way to verify an image of type IMAGE_SELF 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.
+ */
+enum image_type
+{
+    IMAGE_DEV = 0,
+    IMAGE_PROD = 1,
+    IMAGE_BREAKOUT = 2,
+    IMAGE_TEST = 3,
+    IMAGE_SELF = 4
+};
+
+enum hash_type
+{
+    HASH_NONE = 0,
+    HASH_SHA2_224 = 1,
+    HASH_SHA2_256 = 2,
+    HASH_SHA2_384 = 3,
+    HASH_SHA2_512 = 4,
+    HASH_SHA3_224 = 5,
+    HASH_SHA3_256 = 6,
+    HASH_SHA3_384 = 7,
+    HASH_SHA3_512 = 8
+};
+
+/* Note: If the image is of type IMAGE_SELF, the signature_scheme has to be of
+ * type *_ONLY_NO_SIGNATURE.
+ */
+enum signature_scheme
+{
+    SIGNATURE_NONE = 0,
+    SIGNATURE_RSA2048_PKCS15 = 1,
+    SIGNATURE_RSA3072_PKCS15 = 2,
+    SIGNATURE_RSA4096_PKCS15 = 3,
+    SIGNATURE_RSA4096_PKCS15_SHA512 = 4,
+    SHA256_ONLY_NO_SIGNATURE = 5
+};
+
+/* Payload image family. */
+enum image_family
+{
+    IMAGE_FAMILY_ALL = 0,
+    //  values < 256 are reserved for Google-internal use
+};
+
+#define IMAGE_REGION_PROTECTED_ALIGNMENT (4096)
+#define IMAGE_REGION_PROTECTED_PAGE_LENGTH (4096)
+
+struct image_region
+{
+    uint8_t region_name[32]; // null-terminated ASCII string
+    uint32_t region_offset;  // read- and write- protected regions must be
+                             // aligned to IMAGE_REGION_PROTECTED_ALIGNMENT.
+                             // Other regions are also aligned which
+                             // simplifies their implementation.
+    uint32_t region_size;    // read- and write- protected regions must be a
+                             // multiple of IMAGE_REGION_PROTECTED_PAGE_LENGTH.
+    /* Regions will not be persisted across different versions.
+     * This field is intended to flag potential incompatibilities in the
+     * context of data migration (e.g. the ELOG format changed between
+     * two BIOS releases).
+     */
+    uint16_t region_version;
+    /* See IMAGE_REGION_* defines above. */
+    uint16_t region_attributes;
+} __attribute__((__packed__));
+
+/* Main structure (major=1, minor=0). Verification process:
+ * - Hash(image_descriptor + region_count * struct image_region +
+ *        struct hash +
+ *        struct denylist + denylist_size * struct denylist_record +
+ *        struct blob + uint8_t blob[blob_size])
+ * - Verify the signature_* over the hash computed in the previous step
+ * - Compute the rolling hash of the regions marked IMAGE_REGION_STATIC
+ * - The image descriptor is excluded from the hash (descriptor_size bytes)
+ * - Compare the computed hash to the struct hash_*.hash
+ */
+struct image_descriptor
+{
+    uint64_t descriptor_magic; // #define DESCRIPTOR_MAGIC
+    /* Major revisions of this structure are not backwards compatible. */
+    uint8_t descriptor_major;
+    /* Minor revisions of this structure are backwards compatible. */
+    uint8_t descriptor_minor;
+    /* Padding. */
+    uint16_t reserved_0;
+
+    /* This field allows us to mitigate a DOS vector if we end up
+     * scanning the image to discover the image descriptor. The offset
+     * and size are hashed with the rest of the descriptor to prevent
+     * an attacker from copying a valid descriptor to a different
+     * location.
+     *
+     * The offset is relative to the start of the image data.
+     */
+    uint32_t descriptor_offset;
+    /* Includes this struct as well as the auxiliary structs (hash_*,
+     * signature_*, denylist, blob & key_rotation_records). This many bytes
+     * will be skipped when computing the hash of the region this struct
+     * resides in. Tail padding is allowed but must be all 0xff's.
+     */
+    uint32_t descriptor_area_size;
+
+    /*** Image information. ***/
+
+    /* Null-terminated ASCII string. For BIOS this would be the platform
+     * family-genus-version-date (e.g. ixion-hsw-2.8.0-2017.10.03).
+     * Intended for consumption by system software that generates human
+     * readable output (e.g. gsys).
+     */
+    uint8_t image_name[32];
+    /* Image transitions are enforced to be from/to the same family.
+     * 0 is treated as a wildcard (can upgrade to/from any image family).
+     * See image_family enum above.
+     */
+    uint32_t image_family;
+    uint32_t image_major;
+    uint32_t image_minor;
+    uint32_t image_point;
+    uint32_t image_subpoint;
+    /* Seconds since epoch. */
+    uint64_t build_timestamp;
+
+    /* image_type enum { DEV, PROD, BREAKOUT, SELF} */
+    uint8_t image_type;
+    /* 0: no denylist struct, 1: watermark only, >1: watermark + denylist */
+    uint8_t denylist_size;
+    /* hash_type enum { NONE, SHA2_224, SHA2_256, ...} */
+    uint8_t hash_type;
+    /* signature_scheme enum { NONE, RSA2048_PKCS15, ...}
+     * If set, hash_type must be set as well (cannot be NONE).
+     */
+    uint8_t signature_scheme;
+
+    /* struct image_region array size. */
+    uint8_t region_count;
+    uint8_t reserved_1;
+    uint16_t reserved_2;
+    /* The sum of the image_region.region_size fields must add up. */
+    uint32_t image_size;
+    /* Authenticated opaque data exposed to system software. Must be a multiple
+     * of 4 to maintain alignment. Does not include the blob struct magic.
+     */
+    uint32_t blob_size;
+    /* The list is strictly ordered by region_offset.
+     * Must exhaustively describe the image.
+     */
+#ifndef OMIT_VARIABLE_ARRAYS
+    struct image_region image_regions[];
+#endif
+} __attribute__((__packed__));
+
+/* Hash the static regions (IMAGE_REGION_STATIC) excluding this descriptor
+ * structure i.e. skipping image_descriptor.descriptor_size bytes (optional).
+ */
+struct hash_sha256
+{
+    uint32_t hash_magic; // #define HASH_MAGIC
+    uint8_t hash[32];
+} __attribute__((__packed__));
+
+struct hash_sha512
+{
+    uint32_t hash_magic; // #define HASH_MAGIC
+    uint8_t hash[64];
+} __attribute__((__packed__));
+
+struct denylist_record
+{
+    uint32_t image_major;
+    uint32_t image_minor;
+    uint32_t image_point;
+    uint32_t image_subpoint;
+} __attribute__((__packed__));
+
+struct denylist
+{
+    uint32_t denylist_magic; // #define DENYLIST_MAGIC
+    /* Deny list. The first entry is the watermark. All subsequent entries must
+     * be newer than the watermark.
+     */
+#ifndef OMIT_VARIABLE_ARRAYS
+    struct denylist_record denylist_record[];
+#endif
+} __attribute__((__packed__));
+
+struct blob
+{
+    uint32_t blob_magic; // #define BLOB_MAGIC
+#ifndef OMIT_VARIABLE_ARRAYS
+    /* Array of blob_data structures - see blob_data below for details. */
+    uint8_t blobs[];
+#endif
+} __attribute__((__packed__));
+
+/* If blobs[] is non-empty, it is expected to contain one more more instances
+ * of this struct. Each blob_data is followed by the minimum number of padding
+ * bytes (0-3) needed to maintain 4-byte alignment of blob_data structures.
+ * Padding bytes must be 0xFF and must be ignored by readers of blobs[].
+ *
+ * The ordering of the blob_data structures is undefined. Readers of blobs[]
+ * must locate expected blob_data by inspecting blob_type_magic of each
+ * blob_data. Readers are expected to ignore unknown blob_type_magic values,
+ * skipping over them to allow for future types.
+ *
+ * If blob_size is greater than zero but less than sizeof(struct blob_data), the
+ * blobs list is invalid. The blobs list is also invalid if there are multiple
+ * blob_data structures and the last one is truncated due to blob_size being too
+ * small to hold blob_payload_size. Readers must walk the entire length of the
+ * blob_data list to validate the list is well-formed. Any image with an
+ * invalid blobs list has an invalid descriptor and must be treated the same as
+ * an unsigned image.
+ */
+struct blob_data
+{
+    /* BLOB_TYPE_MAGIC_* */
+    uint32_t blob_type_magic;
+    /* Size of the data contained in blob_payload. Need not be a multiple of 4
+     * bytes. Must have sizeof(struct blob_data) + blob_payload_size <=
+     * blob_size.
+     */
+    uint32_t blob_payload_size;
+#ifndef OMIT_VARIABLE_ARRAYS
+    uint8_t blob_payload[];
+#endif
+} __attribute__((__packed__));
+
+/* Signature of the hash of the image_descriptor structure up to and including
+ * this struct but excluding the signature field (optional).
+ */
+struct signature_rsa2048_pkcs15
+{
+    uint32_t signature_magic; // #define SIGNATURE_MAGIC
+    /* Monotonic index of the key used to sign the image (starts at 1). */
+    uint16_t key_index;
+    /* Used to revoke keys, persisted by the enforcer. */
+    uint16_t min_key_index;
+    uint32_t exponent;      // little-endian
+    uint8_t modulus[256];   // big-endian
+    uint8_t signature[256]; // big-endian
+} __attribute__((__packed__));
+
+struct signature_rsa3072_pkcs15
+{
+    uint32_t signature_magic; // #define SIGNATURE_MAGIC
+    /* Monotonic index of the key used to sign the image (starts at 1). */
+    uint16_t key_index;
+    /* Used to revoke keys, persisted by the enforcer. */
+    uint16_t min_key_index;
+    uint32_t exponent;      // little-endian
+    uint8_t modulus[384];   // big-endian
+    uint8_t signature[384]; // big-endian
+} __attribute__((__packed__));
+
+struct signature_rsa4096_pkcs15
+{
+    uint32_t signature_magic; // #define SIGNATURE_MAGIC
+    /* Monotonic index of the key used to sign the image (starts at 1). */
+    uint16_t key_index;
+    /* Used to revoke keys, persisted by the enforcer. */
+    uint16_t min_key_index;
+    uint32_t exponent;      // little-endian
+    uint8_t modulus[512];   // big-endian
+    uint8_t signature[512]; // big-endian
+} __attribute__((__packed__));
+
+struct sha256_only_no_signature
+{
+    uint32_t signature_magic; // #define SIGNATURE_MAGIC
+    uint8_t digest[32];
+} __attribute__((__packed__));
+
+/* Key rotation record (optional).
+ * Enables enforcers to verify images signed with newer (rotated) keys.
+ * The hash function, signature & padding schemes are currently pinned
+ * by image family. This struct is likely to evolve.
+ */
+struct record_rsa2048_pkcs15
+{
+    uint16_t from_index;
+    uint16_t to_index;
+    uint32_t exponent;    // exponent of the new key, little-endian
+    uint8_t modulus[256]; // modulus of the new key, big-endian
+    /* SIGN[K<from_index>](HASH(to_index (LE) | exponent (LE) | modulus (BE)))
+     */
+    uint8_t signature[256]; // big-endian
+} __attribute__((__packed__));
+
+struct record_rsa3072_pkcs15
+{
+    uint16_t from_index;
+    uint16_t to_index;
+    uint32_t exponent;    // exponent of the new key, little-endian
+    uint8_t modulus[384]; // modulus of the new key, big-endian
+    /* SIGN[K<from_index>](HASH(to_index (LE) | exponent (LE) | modulus (BE)))
+     */
+    uint8_t signature[384]; // big-endian
+} __attribute__((__packed__));
+
+struct record_rsa4096_pkcs15
+{
+    uint16_t from_index;
+    uint16_t to_index;
+    uint32_t exponent;    // exponent of the new key, little-endian
+    uint8_t modulus[512]; // modulus of the new key, big-endian
+    /* SIGN[K<from_index>](HASH(to_index (LE) | exponent (LE) | modulus (BE)))
+     */
+    uint8_t signature[512]; // big-endian
+} __attribute__((__packed__));
+
+struct key_rotation_records_rsa2048_pkcs15
+{
+    uint32_t rotation_magic; // #define ROTATION_MAGIC
+    uint16_t record_count;
+    uint16_t reserved_0;
+#ifndef OMIT_VARIABLE_ARRAYS
+    struct record_rsa2048_pkcs15 records[];
+#endif
+} __attribute__((__packed__));
+
+struct key_rotation_records_rsa3072_pkcs15
+{
+    uint32_t rotation_magic; // #define ROTATION_MAGIC
+    uint16_t record_count;
+    uint16_t reserved_0;
+#ifndef OMIT_VARIABLE_ARRAYS
+    struct record_rsa3072_pkcs15 records[];
+#endif
+} __attribute__((__packed__));
+
+struct key_rotation_records_rsa4096_pkcs15
+{
+    uint32_t rotation_magic; // #define ROTATION_MAGIC
+    uint16_t record_count;
+    uint16_t reserved_0;
+#ifndef OMIT_VARIABLE_ARRAYS
+    struct record_rsa3072_pkcs15 records[];
+#endif
+} __attribute__((__packed__));
+#endif // PLATFORMS_SECURITY_TITAN_CR51_IMAGE_DESCRIPTOR_H_
diff --git a/subprojects/libcr51sign/libcr51sign.c b/subprojects/libcr51sign/libcr51sign.c
new file mode 100644
index 0000000..5546f86
--- /dev/null
+++ b/subprojects/libcr51sign/libcr51sign.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "libcr51sign.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifndef USER_PRINT
+#define CPRINTS(ctx, format, args...) printf(format, ##args)
+#endif
+
+#define MEMBER_SIZE(type, field) sizeof(((type*)0)->field)
+
+// True of x is a power of two
+#define POWER_OF_TWO(x) ((x) && !((x) & ((x)-1)))
+
+// 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)
+
+// SPS EEPROM sector size is 4KiB, since this is the smallest erasable size.
+#define IMAGE_REGION_ALIGNMENT 4096
+
+#define MAX_READ_SIZE 1024
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0]))
+#endif
+
+// Values of SIGNATURE_OFFSET shuold be same for all sig types (2048,3072,4096)
+#define SIGNATURE_OFFSET offsetof(struct signature_rsa3072_pkcs15, modulus)
+
+#ifndef BUILD_ASSERT
+#define BUILD_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)]))
+#endif
+
+    typedef enum libcr51sign_validation_failure_reason failure_reason;
+
+    // Returns the bytes size of keys used in the given signature_scheme.
+    // Return error if signature_scheme is invalid.
+    //
+    static failure_reason get_key_size(enum signature_scheme signature_scheme,
+                                       uint16_t* key_size)
+    {
+        switch (signature_scheme)
+        {
+            case SIGNATURE_RSA2048_PKCS15:
+                *key_size = 256;
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA3072_PKCS15:
+                *key_size = 384;
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA4096_PKCS15:
+            case SIGNATURE_RSA4096_PKCS15_SHA512:
+                *key_size = 512;
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
+        }
+    }
+
+    // Returns the hash_type for a given signature scheme
+    // Returns error if scheme is invalid.
+    failure_reason get_hash_type_from_signature(enum signature_scheme scheme,
+                                                enum hash_type* type)
+    {
+        switch (scheme)
+        {
+            case SIGNATURE_RSA2048_PKCS15:
+            case SIGNATURE_RSA3072_PKCS15:
+            case SIGNATURE_RSA4096_PKCS15:
+                *type = HASH_SHA2_256;
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA4096_PKCS15_SHA512:
+                *type = HASH_SHA2_512;
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
+        }
+    }
+
+    // Check if the given hash_type is supported.
+    // Returns error if hash_type is not supported.
+    static failure_reason is_hash_type_supported(enum hash_type type)
+    {
+        switch (type)
+        {
+            case HASH_SHA2_256:
+            case HASH_SHA2_512:
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+        }
+    }
+
+    // Determines digest size for a given hash_type.
+    // Returns error if hash_type is not supported.
+    static failure_reason get_hash_digest_size(enum hash_type type,
+                                               uint32_t* size)
+    {
+        switch (type)
+        {
+            case HASH_SHA2_256:
+                *size = SHA256_DIGEST_SIZE;
+                return LIBCR51SIGN_SUCCESS;
+            case HASH_SHA2_512:
+                *size = SHA512_DIGEST_SIZE;
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+        }
+    }
+
+    // Determines hash struct size for a given hash_type.
+    // Returns error if hash_type is not supported.
+    static failure_reason get_hash_struct_size(enum hash_type type,
+                                               uint32_t* size)
+    {
+        switch (type)
+        {
+            case HASH_SHA2_256:
+                *size = sizeof(struct hash_sha256);
+                return LIBCR51SIGN_SUCCESS;
+            case HASH_SHA2_512:
+                *size = sizeof(struct hash_sha256);
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+        }
+    }
+
+    // Checks that:
+    //  - The signing key is trusted
+    //  - The target version is not denylisted
+    // If validating a staged update, also checks that:
+    //  - The target image family matches the current image family
+    //  - The image type transition is legal (i.e. dev -> *|| prod -> prod) or
+    //    alternatively that the hardware ID is allowlisted
+    // Assuming the caller has performed following:
+    // board_get_base_key_index();
+    // board_get_key_array
+    // Possible return codes:
+    // LIBCR51SIGN_SUCCESS = 0,
+    // LIBCR51SIGN_ERROR_RUNTIME_FAILURE = 1,
+    // LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR = 3,
+    // LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY = 4,
+    // LIBCR51SIGN_ERROR_IMAGE_TYPE_DISALLOWED = 5,
+
+    static failure_reason
+        validate_transition(const struct libcr51sign_ctx* ctx,
+                            const struct libcr51sign_intf* intf,
+                            uint32_t signature_struct_offset)
+    {
+        BUILD_ASSERT((offsetof(struct signature_rsa2048_pkcs15, modulus) ==
+                          SIGNATURE_OFFSET &&
+                      offsetof(struct signature_rsa3072_pkcs15, modulus) ==
+                          SIGNATURE_OFFSET &&
+                      offsetof(struct signature_rsa4096_pkcs15, modulus) ==
+                          SIGNATURE_OFFSET));
+
+        // Read up to the modulus.
+        const uint32_t read_len = SIGNATURE_OFFSET;
+        uint8_t buffer[read_len];
+        // "modulus" & "signature" will not be indexed.
+        struct signature_rsa4096_pkcs15* sig_data = (void*)&buffer;
+        int rv;
+        rv = intf->read(ctx, signature_struct_offset, read_len, buffer);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx,
+                    "validate_transition: failed to read signature struct");
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        if (sig_data->signature_magic != SIGNATURE_MAGIC)
+        {
+            CPRINTS(ctx, "validate_transition: bad signature magic");
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+
+        if (ctx->descriptor.image_family != ctx->current_image_family &&
+            ctx->descriptor.image_family != IMAGE_FAMILY_ALL &&
+            ctx->current_image_family != IMAGE_FAMILY_ALL)
+        {
+            CPRINTS(ctx, "validate_transition: invalid image family");
+            return LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY;
+        }
+
+        if (!intf->is_production_mode)
+        {
+            CPRINTS(ctx, "validate_transition: missing is_production_mode");
+            return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+        }
+        if (intf->is_production_mode() &&
+            (ctx->descriptor.image_type == IMAGE_DEV))
+        {
+            CPRINTS(ctx, "validate_transition: checking exemption allowlist");
+
+            if (!intf->prod_to_dev_downgrade_allowed)
+            {
+                return LIBCR51SIGN_SUCCESS;
+            }
+            else if (!intf->prod_to_dev_downgrade_allowed())
+            {
+                CPRINTS(ctx, "validate_transition: illegal image type");
+                return LIBCR51SIGN_ERROR_DEV_DOWNGRADE_DISALLOWED;
+            }
+        }
+        return LIBCR51SIGN_SUCCESS;
+    }
+
+    // If caller had provided read_and_hash_update call that, otherwise call
+    // read and then update.
+
+    static failure_reason
+        read_and_hash_update(const struct libcr51sign_ctx* ctx,
+                             const struct libcr51sign_intf* intf,
+                             uint32_t offset, uint32_t size)
+    {
+        uint8_t read_buffer[MAX_READ_SIZE];
+        int rv;
+        int read_size;
+
+        if (intf->read_and_hash_update)
+        {
+            rv = intf->read_and_hash_update((void*)ctx, offset, size);
+        }
+        else
+        {
+            if (!intf->hash_update)
+            {
+                CPRINTS(ctx, "read_and_hash_update: missing hash_update");
+                return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+            }
+            do
+            {
+                read_size = size < MAX_READ_SIZE ? size : MAX_READ_SIZE;
+                rv = intf->read((void*)ctx, offset, read_size, read_buffer);
+                if (rv != LIBCR51SIGN_SUCCESS)
+                {
+                    return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+                }
+                rv = intf->hash_update((void*)ctx, read_buffer, read_size);
+                if (rv != LIBCR51SIGN_SUCCESS)
+                {
+                    return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+                }
+                offset += read_size;
+                size -= read_size;
+            } while (size > 0);
+        }
+        return rv;
+    }
+
+    // Validates the image_region array, namely that:
+    //  - The regions are aligned, contiguous & exhaustive
+    //  - That the image descriptor resides in a static region
+    //
+    // 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)
+    {
+        // 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];
+        uint32_t byte_count, region_count, image_size, hash_offset, digest_size;
+        uint8_t d_region_num = 0;
+        int i, rv;
+
+        BUILD_ASSERT((MEMBER_SIZE(struct hash_sha256, hash_magic) ==
+                      MEMBER_SIZE(struct hash_sha512, hash_magic)));
+        image_size = ctx->descriptor.image_size;
+        region_count = ctx->descriptor.region_count;
+        hash_offset = d_offset + sizeof(struct image_descriptor) +
+                      region_count * sizeof(struct image_region);
+        // Read the image_region array.
+        rv = intf->read(
+            ctx, d_offset + offsetof(struct image_descriptor, image_regions),
+            region_count * sizeof(struct image_region), (uint8_t*)&regions);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx,
+                    "validate_payload_regions: failed to read region array");
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+
+        // Validate that the regions are contiguous & exhaustive.
+        for (i = 0, byte_count = 0; i < region_count; 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)
+            {
+                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)
+            {
+                CPRINTS(ctx, "validate_payload_regions: invalid region array");
+                return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+            }
+            byte_count += regions[i].region_size;
+            // The image descriptor must be part of a static region.
+            if (d_offset >= regions[i].region_offset && d_offset < byte_count)
+            {
+                d_region_num = i;
+                CPRINTS(
+                    ctx,
+                    "validate_payload_regions: image descriptor in region %d",
+                    i);
+                // The descriptor can't span regions.
+                if (ctx->descriptor.descriptor_area_size > byte_count ||
+                    !(regions[i].region_attributes & IMAGE_REGION_STATIC))
+                {
+                    CPRINTS(
+                        ctx,
+                        "validate_payload_regions: descriptor must reside in "
+                        "static region");
+                    return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+                }
+            }
+        }
+        if (byte_count != image_size)
+        {
+            CPRINTS(ctx, "validate_payload_regions: invalid image size");
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+
+        rv = get_hash_digest_size(ctx->descriptor.hash_type, &digest_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+
+        rv = intf->read(ctx, hash_offset,
+                        MEMBER_SIZE(struct hash_sha256, hash_magic) +
+                            digest_size,
+                        magic_and_digest);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx,
+                    "validate_payload_regions: failed to read hash from flash");
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        if (*(uint32_t*)magic_and_digest != HASH_MAGIC)
+        {
+            CPRINTS(ctx, "validate_payload_regions: bad hash magic");
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+        rv = intf->hash_init(ctx, ctx->descriptor.hash_type);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate_payload_regions: hash_init failed");
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        for (i = 0; i < region_count; i++)
+        {
+            uint32_t hash_start, hash_size;
+            if (!(regions[i].region_attributes & IMAGE_REGION_STATIC))
+            {
+                continue;
+            }
+            hash_start = regions[i].region_offset;
+            hash_size = regions[i].region_size;
+
+            // Skip the descriptor.
+            do
+            {
+                if (i == d_region_num)
+                {
+                    hash_size = d_offset - hash_start;
+                }
+
+                if (!hash_size)
+                {
+                    hash_start += ctx->descriptor.descriptor_area_size;
+                    hash_size = (regions[i].region_offset +
+                                 regions[i].region_size - hash_start);
+                }
+                CPRINTS("validate_payload_regions: hashing %s (%x - %x)",
+                        regions[i].region_name, hash_start,
+                        hash_start + hash_size);
+                // Read the image_region array.
+                rv = read_and_hash_update(ctx, intf, hash_start, hash_size);
+                if (rv != LIBCR51SIGN_SUCCESS)
+                {
+                    return rv;
+                }
+                hash_start += hash_size;
+            } while (hash_start !=
+                     regions[i].region_offset + regions[i].region_size);
+        }
+        rv = intf->hash_final((void*)ctx, (uint8_t*)dcrypto_digest);
+
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+
+        if (memcmp(magic_and_digest +
+                       MEMBER_SIZE(struct hash_sha256, hash_magic),
+                   dcrypto_digest, digest_size))
+        {
+            CPRINTS(ctx, "validate_payload_regions: invalid hash");
+            return LIBCR51SIGN_ERROR_INVALID_HASH;
+        }
+        // Image is valid.
+        return LIBCR51SIGN_SUCCESS;
+    }
+
+    // Check if the given signature_scheme is supported.
+    // Returns nonzero on error, zero on success
+
+    static failure_reason
+        is_signature_scheme_supported(enum signature_scheme scheme)
+    {
+        switch (scheme)
+        {
+            case SIGNATURE_RSA2048_PKCS15:
+            case SIGNATURE_RSA3072_PKCS15:
+            case SIGNATURE_RSA4096_PKCS15:
+            case SIGNATURE_RSA4096_PKCS15_SHA512:
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
+        }
+    }
+
+    // Returns size of signature struct size in |size|
+    // Returns nonzero on error, zero on success
+
+    static failure_reason
+        get_signature_struct_size(enum signature_scheme scheme, uint32_t* size)
+    {
+        switch (scheme)
+        {
+            case SIGNATURE_RSA2048_PKCS15:
+                *size = sizeof(struct signature_rsa2048_pkcs15);
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA3072_PKCS15:
+                *size = sizeof(struct signature_rsa3072_pkcs15);
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA4096_PKCS15:
+            case SIGNATURE_RSA4096_PKCS15_SHA512:
+                *size = sizeof(struct signature_rsa4096_pkcs15);
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
+        }
+    }
+
+    static failure_reason
+        get_signature_field_offset(enum signature_scheme scheme,
+                                   uint32_t* offset)
+    {
+        switch (scheme)
+        {
+            case SIGNATURE_RSA2048_PKCS15:
+                *offset = offsetof(struct signature_rsa2048_pkcs15, signature);
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA3072_PKCS15:
+                *offset = offsetof(struct signature_rsa3072_pkcs15, signature);
+                return LIBCR51SIGN_SUCCESS;
+            case SIGNATURE_RSA4096_PKCS15:
+            case SIGNATURE_RSA4096_PKCS15_SHA512:
+                *offset = offsetof(struct signature_rsa4096_pkcs15, signature);
+                return LIBCR51SIGN_SUCCESS;
+            default:
+                return LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME;
+        }
+    }
+
+    // Validates the signature (of type scheme) read from "device" at
+    //"raw_signature_offset" with "public_key" over a SHA256/SHA512 digest of
+    // EEPROM area "data_offset:data_size".
+
+    static failure_reason validate_signature(
+        const struct libcr51sign_ctx* ctx, const struct libcr51sign_intf* intf,
+        uint32_t data_offset, uint32_t data_size, enum signature_scheme scheme,
+        uint32_t raw_signature_offset)
+    {
+        uint8_t signature[MAX_SIGNATURE_SIZE];
+        uint16_t key_size;
+        uint32_t digest_size;
+        uint8_t dcrypto_digest[SHA512_DIGEST_SIZE];
+        int rv;
+        enum hash_type hash_type;
+
+        if (!intf->hash_init)
+        {
+            CPRINTS(ctx, "validate_signature: missing hash_init");
+            return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+        }
+        rv = get_hash_type_from_signature(scheme, &hash_type);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(
+                ctx,
+                "validate_payload_regions: hash_type from signature failed");
+            return rv;
+        }
+        rv = intf->hash_init(ctx, hash_type);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate_payload_regions: hash_init failed");
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        rv = read_and_hash_update(ctx, intf, data_offset, data_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate_signature: hash_update failed");
+            return rv;
+        }
+        if (!intf->hash_final)
+        {
+            CPRINTS(ctx, "validate_signature: missing hash_final");
+            return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+        }
+        rv = intf->hash_final((void*)ctx, dcrypto_digest);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate_signature: hash_final failed (status = %d)",
+                    rv);
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        rv = get_key_size(scheme, &key_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+
+        rv = intf->read(ctx, raw_signature_offset, key_size, signature);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(
+                ctx,
+                "validate_signature: failed to read signature (status = %d)",
+                rv);
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        if (!intf->verify_signature)
+        {
+            CPRINTS(ctx, "validate_signature: missing verify_signature");
+            return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+        }
+        rv = get_hash_digest_size(hash_type, &digest_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+        rv = intf->verify_signature(ctx, scheme, signature, key_size,
+                                    dcrypto_digest, digest_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx,
+                    "validate_signature: verification failed (status = %d)",
+                    rv);
+            return LIBCR51SIGN_ERROR_INVALID_SIGNATURE;
+        }
+        CPRINTS(ctx, "validate_signature: verification succeeded");
+        return LIBCR51SIGN_SUCCESS;
+    }
+
+    // Sanity checks the image descriptor & validates its signature.
+    // This function does not validate the image_region array or image hash.
+    //
+    //@param[in] ctx  context which describes the image and holds opaque private
+    //                 data for the user of the library
+    //@param[in] intf  function pointers which interface to the current system
+    // and environment
+    //@param offset  Absolute image descriptor flash offset.
+    //@param relative_offset  Image descriptor offset relative to image start.
+    //@param image_size  Image size in bytes.
+    //@param descriptor  Output pointer to an image_descriptor struct
+
+    static failure_reason validate_descriptor(
+        const struct libcr51sign_ctx* ctx, const struct libcr51sign_intf* intf,
+        uint32_t offset, uint32_t relative_offset, uint32_t image_size)
+    {
+        uint32_t max_descriptor_size, signed_size, signature_scheme,
+            signature_offset;
+        uint32_t signature_struct_offset, signature_struct_size,
+            hash_struct_size;
+        int rv;
+
+        max_descriptor_size = image_size - relative_offset;
+        if (image_size < relative_offset ||
+            max_descriptor_size < sizeof(struct image_descriptor))
+        {
+            CPRINTS(ctx, "validate_descriptor: invalid arguments");
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+
+        rv = intf->read(ctx, offset, sizeof(ctx->descriptor),
+                        (uint8_t*)&ctx->descriptor);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate_descriptor: failed to read descriptor");
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+        }
+        if (ctx->descriptor.descriptor_magic != DESCRIPTOR_MAGIC ||
+            ctx->descriptor.descriptor_offset != relative_offset ||
+            ctx->descriptor.region_count == 0 ||
+            ctx->descriptor.descriptor_area_size > max_descriptor_size ||
+            ctx->descriptor.image_size != image_size)
+        {
+            CPRINTS(ctx, "validate_descriptor: invalid descriptor");
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+        if (ctx->descriptor.image_type != IMAGE_DEV &&
+            ctx->descriptor.image_type != IMAGE_PROD &&
+            ctx->descriptor.image_type != IMAGE_BREAKOUT &&
+            ctx->descriptor.image_type != IMAGE_TEST &&
+            ctx->descriptor.image_type != IMAGE_SELF)
+        {
+            CPRINTS(ctx, "validate_descriptor: bad image type");
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+        // Although the image_descriptor struct supports unauthenticated
+        // images, Haven will not allow it.
+        // Haven only supports SHA256 + RSA2048/RSA3072_PKCS15 currently.
+
+        signature_scheme = ctx->descriptor.signature_scheme;
+
+        rv = is_signature_scheme_supported(signature_scheme);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+        rv = is_hash_type_supported(ctx->descriptor.hash_type);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate_payload_regions: invalid hash type");
+            return rv;
+        }
+        if (ctx->descriptor.descriptor_major > MAX_MAJOR_VERSION ||
+            ctx->descriptor.region_count > MAX_REGION_COUNT)
+        {
+            CPRINTS(ctx, "validate_descriptor: unsupported descriptor");
+            return LIBCR51SIGN_ERROR_UNSUPPORTED_DESCRIPTOR;
+        }
+        rv =
+            get_signature_struct_size(signature_scheme, &signature_struct_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+
+        // Compute the size of the signed portion of the image descriptor.
+        signed_size =
+            sizeof(struct image_descriptor) +
+            ctx->descriptor.region_count * sizeof(struct image_region);
+        rv = get_hash_struct_size(ctx->descriptor.hash_type, &hash_struct_size);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+        signed_size += hash_struct_size;
+        if (ctx->descriptor.denylist_size)
+        {
+            signed_size += sizeof(struct denylist);
+            signed_size +=
+                ctx->descriptor.denylist_size * sizeof(struct denylist_record);
+        }
+        if (ctx->descriptor.blob_size)
+        {
+            signed_size += sizeof(struct blob);
+            // Previous additions are guaranteed not to overflow.
+            if (ctx->descriptor.blob_size >
+                ctx->descriptor.descriptor_area_size - signed_size)
+            {
+                CPRINTS(ctx, "validate_descriptor: invalid blob size (0x%x)",
+                        ctx->descriptor.blob_size);
+                return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+            }
+            signed_size += ctx->descriptor.blob_size;
+        }
+        if (signature_struct_size >
+            ctx->descriptor.descriptor_area_size - signed_size)
+        {
+            CPRINTS(ctx,
+                    "validate_descriptor: invalid descriptor area size "
+                    "(expected = 0x%x, actual = 0x%x)",
+                    ctx->descriptor.descriptor_area_size,
+                    signed_size + signature_struct_size);
+            return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
+        }
+        signature_struct_offset = signed_size;
+        // Omit the actual signature.
+        rv = get_signature_field_offset(signature_scheme, &signature_offset);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+        signed_size += signature_offset;
+
+        // Lookup key & validate transition.
+        rv = validate_transition(ctx, intf, offset + signature_struct_offset);
+
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            return rv;
+        }
+        return validate_signature(ctx, intf, offset, signed_size,
+                                  signature_scheme, offset + signed_size);
+    }
+
+    // Scans the external EEPROM for a magic value at "alignment" boundaries.
+    //
+    //@param device  Handle to the external EEPROM.
+    //@param magic   8-byte pattern to search for.
+    //@param start_offset  Offset to begin searching at.
+    //@param limit   Exclusive address (e.g. EEPROM size).
+    //@param alignment   Alignment boundaries (POW2) to search on.
+    //@param header_offset   Location to place the new header offset.
+    //@return LIBCR51SIGN_SUCCESS (or non-zero on error).
+
+    int scan_for_magic_8(const struct libcr51sign_ctx* ctx,
+                         const struct libcr51sign_intf* intf, uint64_t magic,
+                         uint32_t start_offset, uint32_t limit,
+                         uint32_t alignment, uint32_t* header_offset)
+    {
+        uint64_t read_data;
+        uint32_t offset;
+        int rv;
+
+        if (limit <= start_offset || limit > ctx->end_offset ||
+            limit < sizeof(magic) || !POWER_OF_TWO(alignment))
+        {
+            return LIBCR51SIGN_ERROR_INVALID_ARGUMENT;
+        }
+
+        if (!intf->read)
+        {
+            CPRINTS(ctx, "scan_for_magic_8: missing intf->read");
+            return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+        }
+        // Align start_offset to the next valid boundary.
+        start_offset = ((start_offset - 1) & ~(alignment - 1)) + alignment;
+        for (offset = start_offset; offset < limit - sizeof(magic);
+             offset += alignment)
+        {
+            rv = intf->read((void*)ctx, offset, sizeof(read_data),
+                            (uint8_t*)&read_data);
+            if (rv != LIBCR51SIGN_SUCCESS)
+            {
+                return rv;
+            }
+            if (read_data == magic)
+            {
+                if (header_offset)
+                {
+                    *header_offset = offset;
+                }
+                return LIBCR51SIGN_SUCCESS;
+            }
+        }
+        // Failed to locate magic.
+        return LIBCR51SIGN_ERROR_FAILED_TO_LOCATE_MAGIC;
+    }
+
+    // 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
+    // the rest of the image to verify its integrity.
+    //
+    // @param[in] ctx - context which describes the image and holds opaque
+    // private
+    //                 data for the user of the library
+    // @param[in] intf - function pointers which interface to the current system
+    //                  and environment
+    //
+    // @return nonzero on error, zero on success
+
+    failure_reason libcr51sign_validate(const struct libcr51sign_ctx* ctx,
+                                        struct libcr51sign_intf* intf)
+    {
+        uint32_t image_limit = 0;
+        int rv, rv_first_desc = LIBCR51SIGN_SUCCESS;
+        uint32_t descriptor_offset;
+
+        if (!ctx)
+        {
+            CPRINTS(ctx, "MIssing context");
+            return LIBCR51SIGN_ERROR_INVALID_CONTEXT;
+        }
+        else if (!intf)
+        {
+            CPRINTS(ctx, "Missing interface");
+            return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
+        }
+
+        rv = scan_for_magic_8(ctx, intf, DESCRIPTOR_MAGIC, ctx->start_offset,
+                              ctx->end_offset, DESCRIPTOR_ALIGNMENT,
+                              &descriptor_offset);
+        while (rv == LIBCR51SIGN_SUCCESS)
+        {
+            CPRINTS(ctx, "validate: potential image descriptor found @%x ",
+                    descriptor_offset);
+            // Validation is split into 2 functions to minimize
+            // stack usage.
+
+            rv = validate_descriptor(ctx, intf, descriptor_offset,
+                                     descriptor_offset - ctx->start_offset,
+                                     ctx->end_offset - ctx->start_offset);
+            if (rv != LIBCR51SIGN_SUCCESS)
+            {
+                CPRINTS(ctx, "validate: validate_descriptor() failed ec%d ",
+                        rv);
+            }
+
+            if (rv == LIBCR51SIGN_SUCCESS)
+            {
+                rv = validate_payload_regions(ctx, intf, descriptor_offset);
+                if (rv == LIBCR51SIGN_SUCCESS)
+                {
+                    CPRINTS(ctx, "validate: success!");
+                    return rv;
+                }
+                CPRINTS(ctx,
+                        "validate: validate_payload_regions() failed ec%d ",
+                        rv);
+            }
+            // Store the first desc fail reason if any
+            if (rv != LIBCR51SIGN_SUCCESS &&
+                rv_first_desc == LIBCR51SIGN_SUCCESS)
+                rv_first_desc = rv;
+
+            // scan_for_magic_8() will round up to the next aligned boundary.
+            descriptor_offset++;
+            image_limit = ctx->end_offset - ctx->start_offset;
+            rv = scan_for_magic_8(ctx, intf, DESCRIPTOR_MAGIC,
+                                  descriptor_offset, image_limit,
+                                  DESCRIPTOR_ALIGNMENT, &descriptor_offset);
+        }
+        CPRINTS(ctx, "validate: failed to validate image ec%d ", rv);
+        // If desc validation failed for some reason then return that reason
+        if (rv_first_desc != LIBCR51SIGN_SUCCESS)
+            return rv_first_desc;
+        else
+            return rv;
+    }
+
+    // @func to returns the libcr51sign error code as a string
+    // @param[in] ec - Error code
+    // @return error code in string format
+
+    const char* libcr51sign_errorcode_to_string(failure_reason ec)
+    {
+        switch (ec)
+        {
+            case LIBCR51SIGN_SUCCESS:
+                return "Success";
+            case LIBCR51SIGN_ERROR_RUNTIME_FAILURE:
+                return "Runtime Error Failure";
+            case LIBCR51SIGN_ERROR_UNSUPPORTED_DESCRIPTOR:
+                return "Unsupported descriptor";
+            case LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR:
+                return "Invalid descriptor";
+            case LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY:
+                return "Invalid image family";
+            case LIBCR51SIGN_ERROR_IMAGE_TYPE_DISALLOWED:
+                return "Image type disallowed";
+            case LIBCR51SIGN_ERROR_DEV_DOWNGRADE_DISALLOWED:
+                return "Dev downgrade disallowed";
+            case LIBCR51SIGN_ERROR_UNTRUSTED_KEY:
+                return "Untrusted key";
+            case LIBCR51SIGN_ERROR_INVALID_SIGNATURE:
+                return "Invalid signature";
+            case LIBCR51SIGN_ERROR_INVALID_HASH:
+                return "Invalid hash";
+            case LIBCR51SIGN_ERROR_INVALID_HASH_TYPE:
+                return "Invalid hash type";
+            case LIBCR51SIGN_ERROR_INVALID_ARGUMENT:
+                return "Invalid Argument";
+            case LIBCR51SIGN_ERROR_FAILED_TO_LOCATE_MAGIC:
+                return "Failed to locate descriptor";
+            case LIBCR51SIGN_ERROR_INVALID_CONTEXT:
+                return "Invalid context";
+            case LIBCR51SIGN_ERROR_INVALID_INTERFACE:
+                return "Invalid interface";
+            case LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME:
+                return "Invalid signature scheme";
+            default:
+                return "Unknown error";
+        }
+    }
+
+#ifdef __cplusplus
+} //  extern "C"
+#endif
diff --git a/subprojects/libcr51sign/libcr51sign.h b/subprojects/libcr51sign/libcr51sign.h
new file mode 100644
index 0000000..7210d26
--- /dev/null
+++ b/subprojects/libcr51sign/libcr51sign.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef PLATFORMS_HAVEN_LIBCR51SIGN_LIBCR51SIGN_H_
+#define PLATFORMS_HAVEN_LIBCR51SIGN_LIBCR51SIGN_H_
+
+#include "cr51_image_descriptor.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define SHA256_DIGEST_SIZE 32
+#define SHA512_DIGEST_SIZE 64
+
+// Currently RSA4096 (in bytes).
+#define MAX_SIGNATURE_SIZE 512
+
+    // List of common error codes that can be returned
+    enum libcr51sign_validation_failure_reason
+    {
+        // All PayloadRegionState fields are valid & authenticated.
+        LIBCR51SIGN_SUCCESS = 0,
+
+        // Descriptor sanity check failed. None of the following
+        // PayloadRegionState fields are valid/populated.
+        LIBCR51SIGN_ERROR_RUNTIME_FAILURE = 1,
+        LIBCR51SIGN_ERROR_UNSUPPORTED_DESCRIPTOR = 2,
+        LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR = 3,
+
+        // All fields are populated but may not be authentic.
+        LIBCR51SIGN_ERROR_INVALID_IMAGE_FAMILY = 4,
+        LIBCR51SIGN_ERROR_IMAGE_TYPE_DISALLOWED = 5,
+        LIBCR51SIGN_ERROR_DEV_DOWNGRADE_DISALLOWED = 6,
+        LIBCR51SIGN_ERROR_UNTRUSTED_KEY = 7,
+        LIBCR51SIGN_ERROR_INVALID_SIGNATURE = 8,
+        LIBCR51SIGN_ERROR_INVALID_HASH = 9,
+        LIBCR51SIGN_ERROR_INVALID_HASH_TYPE = 10,
+        // Invalid argument
+        LIBCR51SIGN_ERROR_INVALID_ARGUMENT = 11,
+        LIBCR51SIGN_ERROR_FAILED_TO_LOCATE_MAGIC = 12,
+        LIBCR51SIGN_ERROR_INVALID_CONTEXT = 13,
+        LIBCR51SIGN_ERROR_INVALID_INTERFACE = 14,
+        LIBCR51SIGN_ERROR_INVALID_SIG_SCHEME = 15,
+        LIBCR51SIGN_ERROR_MAX = 16,
+    };
+
+    struct libcr51sign_ctx
+    {
+        // Absolute image start offset
+        uint32_t start_offset;
+        // Absolute image end offset
+        uint32_t end_offset;
+        size_t block_size;
+        enum image_family current_image_family;
+        enum image_type current_image_type;
+        // keyring_len - number of keys in @a keyring
+        int keyring_len;
+        // valid_key - index of valid key on success
+        size_t* valid_key;
+        // keyring - array of pointers to public keys
+        const void* keyring;
+        void* priv;
+        struct image_descriptor descriptor;
+    };
+
+    struct libcr51sign_intf
+    {
+        // @func read read data from the image into a buffer
+        //
+        // @param[in] ctx - context struct
+        // @param[in] offset - bytes to seek into the image before reading
+        // @param[in] count - number of bytes to read
+        // @param[out] buf - pointer to buffer where result will be written
+        //
+        // @return nonzero on error, zero on success
+
+        int (*read)(const void*, uint32_t, uint32_t, uint8_t*);
+
+        // @func hash_init get ready to compute a hash
+        //
+        // @param[in] ctx - context struct
+        // @param[in] hash_type - type of hash function to use
+        //
+        // @return nonzero on error, zero on success
+
+        int (*hash_init)(const void*, enum hash_type);
+
+        // @func hash_update add data to the hash
+        //
+        // @param[in] ctx - context struct
+        // @param[in] buf - data to add to hash
+        // @param[in] count - number of bytes of data to add
+        // @param[in] hash_type - type of hash function to use
+        //
+        // @return nonzero on error, zero on success
+
+        int (*hash_update)(void*, const uint8_t*, size_t);
+
+        // Note this is a combination of an spi_nor_read() with
+        // spi_transaction() It is the responsibility of the caller to
+        // synchronize with other potential SPI clients / transactions.
+        // Collapsing the SPI stack results in a 2x throughput improvement (~20s
+        // -> ~10s to verify an Indus image with SHA256 HW acceleration).
+        //
+        // The caller is responsible for calling DCRYPTO_init()/HASH_final().
+
+        int (*read_and_hash_update)(void* ctx, uint32_t offset, uint32_t size);
+
+        // @func hash_final finish hash calculation
+        //
+        // @param[in] ctx - context struct
+        // @param[out] hash - buffer to write hash to
+        // @param[in] hash_type - type of hash function to use
+        //
+        // @return nonzero on error, zero on success
+
+        int (*hash_final)(void*, uint8_t*);
+
+        // @func verify check that the signature is valid for given hashed data
+        //
+        // @param[in] ctx - context struct
+        // @param[in] scheme - type of signature, hash, etc.
+        // @param[in] sig - signature blob
+        // @param[in] sig_len - length of signature in bytes
+        // @param[in] data - pre-hashed data to verify
+        // @param[in] data_len - length of hashed data in bytes
+        //
+        // @return nonzero on error, zero on success
+
+        int (*verify_signature)(const void*, enum signature_scheme,
+                                const uint8_t*, size_t, const uint8_t*, size_t);
+
+        // @func verify check that if the prod to dev downgrade/ hardware
+        // allowlist is allowed
+        // @return  true: if allowed
+        //          false: if not allowed
+        // BMC would return always false or pass a NULL pointer
+        // If NULL, treated as if the function always returns false.
+
+        bool (*prod_to_dev_downgrade_allowed)();
+
+        // @func returns true if the current firmware is running in production
+        // mode.
+        // @return true: if in production mode
+        //         false: if in any non-production mode
+
+        bool (*is_production_mode)();
+    };
+
+    // 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
+    // the rest of the image to verify its integrity.
+    //
+    // @param[in] ctx - context which describes the image and holds opaque
+    // private
+    //                  data for the user of the library
+    // @param[in] intf - function pointers which interface to the current system
+    //                   and environment
+    //
+    // @return nonzero on error, zero on success
+
+    enum libcr51sign_validation_failure_reason
+        libcr51sign_validate(const struct libcr51sign_ctx* ctx,
+                             struct libcr51sign_intf* intf);
+
+    // Function to convert error code to string format
+    // @param[in] ec - error code
+    // @return error code in string format
+
+    const char* libcr51sign_errorcode_to_string(
+        enum libcr51sign_validation_failure_reason ec);
+
+    // Returns the hash_type for a given signature scheme
+    // @param[in] scheme - signature scheme
+    // @param[out] type - hash_type supported by given signature_scheme
+    //
+    // @return nonzero on error, zero on success
+
+    enum libcr51sign_validation_failure_reason
+        get_hash_type_from_signature(enum signature_scheme scheme,
+                                     enum hash_type* type);
+
+#ifdef __cplusplus
+} //  extern "C"
+#endif
+
+#endif // PLATFORMS_HAVEN_LIBCR51SIGN_LIBCR51SIGN_H_
diff --git a/subprojects/libcr51sign/libcr51sign_support.c b/subprojects/libcr51sign/libcr51sign_support.c
new file mode 100644
index 0000000..3c968d3
--- /dev/null
+++ b/subprojects/libcr51sign/libcr51sign_support.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "libcr51sign_support.h"
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    // @func hash_init get ready to compute a hash
+    //
+    // @param[in] ctx - context struct
+    // @param[in] hash_type - type of hash function to use
+    //
+    // @return nonzero on error, zero on success
+
+    int hash_init(const void* ctx, enum hash_type type)
+    {
+        struct libcr51sign_ctx* context = (struct libcr51sign_ctx*)ctx;
+        struct hash_ctx* hash_context = (struct hash_ctx*)context->priv;
+        hash_context->hash_type = type;
+        if (type == HASH_SHA2_256) // SHA256_Init returns 1
+            SHA256_Init(&hash_context->sha256_ctx);
+        else if (type == HASH_SHA2_512)
+            SHA512_Init(&hash_context->sha512_ctx);
+        else
+            return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+
+        return LIBCR51SIGN_SUCCESS;
+    }
+
+    // @func hash_update add data to the hash
+    //
+    // @param[in] ctx - context struct
+    // @param[in] buf - data to add to hash
+    // @param[in] count - number of bytes of data to add
+    //
+    // @return nonzero on error, zero on success
+
+    int hash_update(void* ctx, const uint8_t* data, size_t size)
+    {
+        if (size == 0)
+            return LIBCR51SIGN_SUCCESS;
+        struct libcr51sign_ctx* context = (struct libcr51sign_ctx*)ctx;
+        struct hash_ctx* hash_context = (struct hash_ctx*)context->priv;
+
+        if (hash_context->hash_type == HASH_SHA2_256) // SHA256_Update returns 1
+            SHA256_Update(&hash_context->sha256_ctx, data, size);
+        else if (hash_context->hash_type == HASH_SHA2_512)
+            SHA512_Update(&hash_context->sha512_ctx, data, size);
+        else
+            return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+
+        return LIBCR51SIGN_SUCCESS;
+    }
+
+    // @func hash_final finish hash calculation
+    //
+    // @param[in] ctx - context struct
+    // @param[out] hash - buffer to write hash to (guaranteed to be big enough)
+    //
+    // @return nonzero on error, zero on success
+
+    int hash_final(void* ctx, uint8_t* hash)
+    {
+        int rv;
+        struct libcr51sign_ctx* context = (struct libcr51sign_ctx*)ctx;
+        struct hash_ctx* hash_context = (struct hash_ctx*)context->priv;
+
+        if (hash_context->hash_type == HASH_SHA2_256)
+            rv = SHA256_Final(hash, &hash_context->sha256_ctx);
+        else if (hash_context->hash_type == HASH_SHA2_512)
+            rv = SHA512_Final(hash, &hash_context->sha512_ctx);
+        else
+            return LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+
+        if (rv)
+            return LIBCR51SIGN_SUCCESS;
+        else
+            return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+    }
+
+    // @func verify check that the signature is valid for given hashed data
+    //
+    // @param[in] ctx - context struct
+    // @param[in] scheme - type of signature, hash, etc.
+    // @param[in] sig - signature blob
+    // @param[in] sig_len - length of signature in bytes
+    // @param[in] data - pre-hashed data to verify
+    // @param[in] data_len - length of hashed data in bytes
+    //
+    // verify_signature expects RSA public key file path in ctx->key_ring
+    // @return nonzero on error, zero on success
+
+    int verify_signature(const void* ctx, enum signature_scheme sig_scheme,
+                         const uint8_t* sig, size_t sig_len,
+                         const uint8_t* data, size_t data_len)
+    {
+        // By default returns error.
+        int rv = LIBCR51SIGN_ERROR_INVALID_ARGUMENT;
+
+        printf("\n sig_len %zu sig: ", sig_len);
+        for (int i = 0; i < sig_len; i++)
+        {
+            printf("%x", sig[i]);
+        }
+
+        struct libcr51sign_ctx* lctx = (struct libcr51sign_ctx*)ctx;
+        FILE* fp = fopen(lctx->keyring, "r");
+        RSA *rsa = NULL, *pub_rsa = NULL;
+        EVP_PKEY* pkey = NULL;
+        BIO* bio = BIO_new(BIO_s_mem());
+        if (!fp)
+        {
+            printf("\n fopen failed: ");
+            goto clean_up;
+        }
+
+        pkey = PEM_read_PUBKEY(fp, 0, 0, 0);
+        if (!pkey)
+        {
+            printf("\n Read public key failed: ");
+            goto clean_up;
+        }
+
+        rsa = EVP_PKEY_get1_RSA(pkey);
+        if (!rsa)
+        {
+            goto clean_up;
+        }
+        pub_rsa = RSAPublicKey_dup(rsa);
+        if (!RSA_print(bio, pub_rsa, 2))
+        {
+            printf("\n RSA print failed ");
+        }
+        if (!pub_rsa)
+        {
+            printf("\n no pub rsa: ");
+            goto clean_up;
+        }
+        printf("\n public rsa \n");
+        char buffer[1024];
+        while (BIO_read(bio, buffer, sizeof(buffer) - 1) > 0)
+        {
+            printf(" %s", buffer);
+        }
+        enum hash_type hash_type;
+        rv = get_hash_type_from_signature(sig_scheme, &hash_type);
+        if (rv != LIBCR51SIGN_SUCCESS)
+        {
+            printf("\n Invalid hash_type! \n");
+            goto clean_up;
+        }
+        int hash_nid = -1;
+        if (hash_type == HASH_SHA2_256)
+        {
+            hash_nid = NID_sha256;
+        }
+        else if (hash_type == HASH_SHA2_512)
+        {
+            hash_nid = NID_sha512;
+        }
+        else
+        {
+            rv = LIBCR51SIGN_ERROR_INVALID_HASH_TYPE;
+            goto clean_up;
+        }
+
+        int ret = RSA_verify(hash_nid, data, data_len, sig, sig_len, pub_rsa);
+        // OpenSSL RSA_verify returns 1 on success and 0 on failure
+        if (!ret)
+        {
+            printf("\n OPENSSL_ERROR: %s \n",
+                   ERR_error_string(ERR_get_error(), NULL));
+            rv = LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
+            goto clean_up;
+        }
+        rv = LIBCR51SIGN_SUCCESS;
+        printf("\n sig: ");
+        for (int i = 0; i < sig_len; i++)
+        {
+            printf("%x", sig[i]);
+        }
+
+        printf("\n data: ");
+        for (int i = 0; i < data_len; i++)
+        {
+            printf("%x", data[i]);
+        }
+        const unsigned rsa_size = RSA_size(pub_rsa);
+        printf("\n rsa size %d sig_len %d", rsa_size, (uint32_t)sig_len);
+
+    clean_up:
+        if (fp)
+        {
+            fclose(fp);
+        }
+        EVP_PKEY_free(pkey);
+        RSA_free(rsa);
+        RSA_free(pub_rsa);
+        BIO_free(bio);
+        return rv;
+    }
+
+#ifdef __cplusplus
+} //  extern "C"
+#endif
diff --git a/subprojects/libcr51sign/libcr51sign_support.h b/subprojects/libcr51sign/libcr51sign_support.h
new file mode 100644
index 0000000..87b4401
--- /dev/null
+++ b/subprojects/libcr51sign/libcr51sign_support.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef PLATFORMS_HAVEN_LIBCR51SIGN_LIBCR51SIGN_SUPPORT_H_
+#define PLATFORMS_HAVEN_LIBCR51SIGN_LIBCR51SIGN_SUPPORT_H_
+
+#include "libcr51sign.h"
+
+#include <openssl/sha.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    struct hash_ctx
+    {
+        enum hash_type hash_type;
+        union
+        {
+            SHA256_CTX sha256_ctx;
+            SHA512_CTX sha512_ctx;
+        };
+    };
+
+    // @func hash_init get ready to compute a hash
+    //
+    // @param[in] ctx - context struct
+    // @param[in] hash_type - type of hash function to use
+    //
+    // @return nonzero on error, zero on success
+
+    int hash_init(const void* ctx, enum hash_type type);
+
+    // @func hash_update add data to the hash
+    //
+    // @param[in] ctx - context struct
+    // @param[in] buf - data to add to hash
+    // @param[in] count - number of bytes of data to add
+    //
+    // @return nonzero on error, zero on success
+
+    int hash_update(void* ctx, const uint8_t* data, size_t size);
+
+    // @func hash_final finish hash calculation
+    //
+    // @param[in] ctx - context struct
+    // @param[out] hash - buffer to write hash to (guaranteed to be big enough)
+    //
+    // @return nonzero on error, zero on success
+
+    int hash_final(void* ctx, uint8_t* hash);
+
+    // @func verify check that the signature is valid for given hashed data
+    //
+    // @param[in] ctx - context struct
+    // @param[in] scheme - type of signature, hash, etc.
+    // @param[in] sig - signature blob
+    // @param[in] sig_len - length of signature in bytes
+    // @param[in] data - pre-hashed data to verify
+    // @param[in] data_len - length of hashed data in bytes
+    //
+    // @return nonzero on error, zero on success
+
+    int verify_signature(const void* ctx, enum signature_scheme sig_scheme,
+                         const uint8_t* sig, size_t sig_len,
+                         const uint8_t* data, size_t data_len);
+
+#ifdef __cplusplus
+} //  extern "C"
+#endif
+#endif // PLATFORMS_HAVEN_LIBCR51SIGN_LIBCR51SIGN_SUPPORT_H_
diff --git a/subprojects/libcr51sign/meson.build b/subprojects/libcr51sign/meson.build
new file mode 100644
index 0000000..a96a204
--- /dev/null
+++ b/subprojects/libcr51sign/meson.build
@@ -0,0 +1,50 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+project('libcr51sign', 'c',
+        version: '0.1', meson_version: '>=0.57.0',
+        default_options: [
+          'warning_level=2',
+          'werror=false',
+          'cpp_std=c++20'
+        ])
+
+# libssl part of openssl isn't used.
+openssl = dependency('libcrypto')
+
+libcr51sign_headers = include_directories('.')
+
+libcr51sign_lib = library(
+  'libcr51sign',
+  'libcr51sign.c',
+  'libcr51sign_support.c',
+  include_directories: libcr51sign_headers,
+  dependencies: openssl,
+  implicit_include_directories: false,
+  version: meson.project_version(),
+  install: true)
+
+pkg = import('pkgconfig')
+pkg.generate(
+  libcr51sign_lib,
+  name: 'libcr51sign',
+  description: 'CR51 signing verification utilities',
+  requires: openssl,
+  version: meson.project_version())
+
+install_headers(
+  'cr51_image_descriptor.h',
+  'libcr51sign.h',
+  'libcr51sign_support.h',
+  subdir: 'libcr51sign')
diff --git a/subprojects/libcr51sign/subprojects b/subprojects/libcr51sign/subprojects
new file mode 120000
index 0000000..a96aa0e
--- /dev/null
+++ b/subprojects/libcr51sign/subprojects
@@ -0,0 +1 @@
+..
\ No newline at end of file