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/.gitignore b/.gitignore
index 210bc0f..d73d627 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
build*/
subprojects/*
!subprojects/acpi-power-state-daemon/
+!subprojects/libcr51sign/
!subprojects/metrics-ipmi-blobs/
!subprojects/ncsid/
!subprojects/nemora-postd/
diff --git a/libcr51sign b/libcr51sign
new file mode 120000
index 0000000..f3dc932
--- /dev/null
+++ b/libcr51sign
@@ -0,0 +1 @@
+./subprojects/libcr51sign/
\ No newline at end of file
diff --git a/meson.build b/meson.build
index 76719a2..f84a2d3 100644
--- a/meson.build
+++ b/meson.build
@@ -38,3 +38,4 @@
subproject('ncsid', default_options: 'tests=' + tests_str)
subproject('metrics-ipmi-blobs', default_options: 'tests=' + tests_str)
subproject('nemora-postd')
+subproject('libcr51sign')
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*)®ions);
+ 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