libcr51sign: enhance to support key rotation

Support validate cr51 descriptor with trusted hash and key within the
signature structure.

Accept the descriptor if its hash is trusted.
Validate the descriptor signature with verification key provided along
with the signature if the key is trusted.

To support the above enhancement for key rotation, three new functions
are defined in interface:

* trust_descriptor_hash
* trust_key_in_signature_structure
* verify_rsa_signature_with_modulus_and_exponent

Applications (i.e. flashupdate or bios-validator) will provide these
functions.

And to facilitate verify rsa signature using key in signature structure,
an implementation of verify_rsa_signature_with_modulus_and_exponent
using the openssl has been provided also.

Change-Id: I787f8c661433052f8c8a1d23e9e6140befce2265
Signed-off-by: Dan Zhang <zhdaniel@google.com>
diff --git a/subprojects/libcr51sign/include/libcr51sign/libcr51sign.h b/subprojects/libcr51sign/include/libcr51sign/libcr51sign.h
index e1e1ace..defaf30 100644
--- a/subprojects/libcr51sign/include/libcr51sign/libcr51sign.h
+++ b/subprojects/libcr51sign/include/libcr51sign/libcr51sign.h
@@ -19,6 +19,7 @@
 #include <libcr51sign/cr51_image_descriptor.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C"
@@ -33,7 +34,21 @@
 // Currently RSA4096 (in bytes).
 #define LIBCR51SIGN_MAX_SIGNATURE_SIZE 512
 
+// LINT.IfChange(image_mauv_max_size_def)
 #define IMAGE_MAUV_DATA_MAX_SIZE (128)
+// LINT.ThenChange()
+
+// State of the image.
+enum libcr51sign_validation_state
+{
+    LIBCR51SIGN_IMAGE_UNSPECIFIED = 0,
+    // The image fails at least one descriptor or region check.
+    LIBCR51SIGN_IMAGE_INVALID = 1,
+    // The image passes all descriptor and region checks. Note that this does
+    // not mean that the image is valid for update. For example, the image may
+    // not pass MAUV checks.
+    LIBCR51SIGN_IMAGE_VALID = 2,
+};
 
 // List of common error codes that can be returned
 enum libcr51sign_validation_failure_reason
@@ -72,26 +87,29 @@
     LIBCR51SIGN_ERROR_STORED_IMAGE_MAUV_EXPECTS_PAYLOAD_IMAGE_MAUV = 23,
     // Client did not find any stored MAUV in system
     LIBCR51SIGN_NO_STORED_MAUV_FOUND = 24,
-    LIBCR51SIGN_ERROR_MAX = 25,
+    LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR_BLOBS = 25,
+    LIBCR51SIGN_ERROR_MAX = 26,
 };
 
 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;
+    // Expectations needed to validate an image. Users must set these fields
+    // before calling libcr51sign_validate().
+    uint32_t start_offset;                  // Absolute image start offset
+    uint32_t end_offset;                    // Absolute image end offset
+    enum image_family current_image_family; // Expected image family
+    enum image_type current_image_type;     // Expected image type
+    int keyring_len;     // keyring_len - number of keys in @a keyring
+    const void* keyring; // keyring - array of pointers to public keys
+    void* priv;          // opaque context data (used for hash state)
+
+    // Data that is accessible if the image is valid after calling
+    // libcr51sign_validate().
+    enum libcr51sign_validation_state validation_state;
+    size_t* valid_key; // valid_key - index of valid key
+    // Note: `descriptor` needs to be the last member of this struct due to the
+    // flexible array member in struct image_descriptor.
+    struct image_descriptor descriptor; // Cr51 image descriptor.
 };
 
 struct libcr51sign_intf
@@ -210,6 +228,45 @@
     //                              Non-zero value otherwise
     int (*store_new_image_mauv_data)(const void*, const uint8_t* const,
                                      const uint32_t);
+
+    // @func trust descriptor hash
+    // @param[in]  ctx - context struct
+    // @param[in]  descriptor_hash - Buffer containing descriptor hash
+    // @param[in]  descriptor_hash_size - Size of descriptor hash
+    //
+    // @return true: if the external key is trusted
+    //         false: if the external key is not trusted
+    bool (*trust_descriptor_hash)(const void*, const uint8_t*, size_t);
+
+    // @func Trust key in the signature structure
+    // @param[in]  ctx - context struct
+    // @param[in]  scheme - signature scheme
+    // @param[in]  signature_structure - signature structure
+    // @param[in]  signature_structure_size - Size of signature structure in
+    // bytes
+    //
+    // @return true: if the key in signature structure is trusted
+    //         false: if the key in signature structure is not trusted
+    bool (*trust_key_in_signature_structure)(
+        const void*, enum signature_scheme scheme, const void*, size_t);
+
+    // @func Verify RSA signature with modulus and exponent
+    // @param[in]  ctx - context struct
+    // @param[in]  sig_scheme - signature scheme
+    // @param[in]  modulus - modulus of the RSA key, MSB (big-endian)
+    // @param[in]  modulus_len - length of modulus in bytes
+    // @param[in]  exponent - exponent of the RSA key
+    // @param[in]  sig - signature blob
+    // @param[in]  sig_len - length of signature in bytes
+    // @param[in]  digest - digest to verify
+    // @param[in]  digest_len - digest size
+    //
+    // @return true: if the signature is verified
+    //         false: otherwise
+    bool (*verify_rsa_signature_with_modulus_and_exponent)(
+        const void* ctx, enum signature_scheme scheme, const uint8_t* modulus,
+        int modulus_len, uint32_t exponent, const uint8_t* sig, int sig_len,
+        const uint8_t* digest, int digest_len);
 };
 
 struct libcr51sign_validated_regions
@@ -232,7 +289,7 @@
 // @return nonzero on error, zero on success
 
 enum libcr51sign_validation_failure_reason libcr51sign_validate(
-    const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
+    struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
     struct libcr51sign_validated_regions* image_regions);
 
 // Function to convert error code to string format
diff --git a/subprojects/libcr51sign/include/libcr51sign/libcr51sign_support.h b/subprojects/libcr51sign/include/libcr51sign/libcr51sign_support.h
index 5a58159..9841730 100644
--- a/subprojects/libcr51sign/include/libcr51sign/libcr51sign_support.h
+++ b/subprojects/libcr51sign/include/libcr51sign/libcr51sign_support.h
@@ -19,6 +19,9 @@
 
 #include <libcr51sign/libcr51sign.h>
 #include <openssl/sha.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C"
@@ -78,6 +81,25 @@
                      const uint8_t* sig, size_t sig_len, const uint8_t* data,
                      size_t data_len);
 
+// @func verify the rsa signature with provided modulus and exponent.
+//
+// @param[in] ctx - context struct
+// @param[in] scheme - type of signature, hash, etc.
+// @param[in] modulus - modulus of the RSA key
+// @param[in] modulus_len - length of modulus in bytes
+// @param[in] exponent - exponent of the RSA key
+// @param[in] sig - signature blob
+// @param[in] sig_len - length of signature in bytes
+// @param[in] digest - digest to verify
+// @param[in] digest_len - digest size
+//
+// @return true if verified, otherwise false.
+
+__attribute__((nonnull)) bool verify_rsa_signature_with_modulus_and_exponent(
+    const void* ctx, enum signature_scheme sig_scheme, const uint8_t* modulus,
+    int modulus_len, uint32_t exponent, const uint8_t* sig, int sig_len,
+    const uint8_t* digest, int digest_len);
+
 #ifdef __cplusplus
 } //  extern "C"
 #endif
diff --git a/subprojects/libcr51sign/src/libcr51sign.c b/subprojects/libcr51sign/src/libcr51sign.c
index 77f5812..5c41a04 100644
--- a/subprojects/libcr51sign/src/libcr51sign.c
+++ b/subprojects/libcr51sign/src/libcr51sign.c
@@ -13,14 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#include "stddef.h"
-
 #include <assert.h>
 #include <libcr51sign/cr51_image_descriptor.h>
 #include <libcr51sign/libcr51sign.h>
 #include <libcr51sign/libcr51sign_internal.h>
 #include <libcr51sign/libcr51sign_mauv.h>
+#include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -49,7 +47,7 @@
 #define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0]))
 #endif
 
-// Values of SIGNATURE_OFFSET shuold be same for all sig types (2048,3072,4096)
+// Values of SIGNATURE_OFFSET should be same for all sig types (2048,3072,4096)
 #define SIGNATURE_OFFSET offsetof(struct signature_rsa3072_pkcs15, modulus)
 
 #ifndef BUILD_ASSERT
@@ -180,15 +178,15 @@
     {
         read_len = SIGNATURE_OFFSET
     };
-    uint8_t buffer[read_len];
+    uint32_t buffer[read_len / sizeof(uint32_t)];
     int rv;
-    rv = intf->read(ctx, signature_struct_offset, read_len, buffer);
+    rv = intf->read(ctx, signature_struct_offset, read_len, (uint8_t*)buffer);
     if (rv != LIBCR51SIGN_SUCCESS)
     {
         CPRINTS(ctx, "%s: failed to read signature struct\n", __FUNCTION__);
         return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
     }
-    if (*(uint32_t*)buffer != SIGNATURE_MAGIC)
+    if (*buffer != SIGNATURE_MAGIC)
     {
         CPRINTS(ctx, "%s: bad signature magic\n", __FUNCTION__);
         return LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR;
@@ -232,7 +230,7 @@
 {
     uint8_t read_buffer[MAX_READ_SIZE];
     int rv;
-    int read_size;
+    uint32_t read_size;
 
     if (intf->read_and_hash_update)
     {
@@ -276,13 +274,13 @@
     const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
     uint32_t d_offset, struct libcr51sign_validated_regions* image_regions)
 {
-    // Allocate buffer to accomodate largest supported hash-type(SHA512)
+    // Allocate buffer to accommodate largest supported hash-type(SHA512)
     uint8_t magic_and_digest[MEMBER_SIZE(struct hash_sha512, hash_magic) +
                              LIBCR51SIGN_SHA512_DIGEST_SIZE];
     uint8_t dcrypto_digest[LIBCR51SIGN_SHA512_DIGEST_SIZE];
     uint32_t byte_count, region_count, image_size, hash_offset, digest_size;
     uint32_t i;
-    uint8_t d_region_num = 0;
+    uint32_t d_region_num = 0;
     int rv;
     struct image_region const* region;
 
@@ -438,7 +436,7 @@
     }
 
     if (memcmp(magic_and_digest + MEMBER_SIZE(struct hash_sha256, hash_magic),
-               dcrypto_digest, digest_size))
+               dcrypto_digest, digest_size) != 0)
     {
         CPRINTS(ctx, "%s: invalid hash\n", __FUNCTION__);
         return LIBCR51SIGN_ERROR_INVALID_HASH;
@@ -536,6 +534,118 @@
     }
 }
 
+__attribute__((nonnull)) static bool is_key_in_signature_struct_trusted(
+    const struct libcr51sign_ctx* ctx, const struct libcr51sign_intf* intf,
+    enum signature_scheme scheme, uint32_t raw_signature_offset,
+    void* signature_struct, uint32_t* signature_struct_size)
+{
+    if (!intf->trust_key_in_signature_structure)
+    {
+        CPRINTS(ctx, "%s: trust_key_in_signature_structure is not supported\n",
+                __FUNCTION__);
+        return false;
+    }
+
+    uint32_t signature_field_offset;
+    int rv = get_signature_field_offset(scheme, &signature_field_offset);
+    if (rv != LIBCR51SIGN_SUCCESS)
+    {
+        return false;
+    }
+
+    if (signature_field_offset > raw_signature_offset)
+    {
+        CPRINTS(ctx,
+                "%s: signature_field_offset (%d) is larger than "
+                "raw_signature_offset (%d)\n",
+                __FUNCTION__, signature_field_offset, raw_signature_offset);
+        return false;
+    }
+    uint32_t signature_offset = raw_signature_offset - signature_field_offset;
+
+    rv = get_signature_struct_size(scheme, signature_struct_size);
+    if (rv != LIBCR51SIGN_SUCCESS)
+    {
+        return false;
+    }
+
+    rv = intf->read(ctx, signature_offset, *signature_struct_size,
+                    signature_struct);
+    if (rv != LIBCR51SIGN_SUCCESS)
+    {
+        CPRINTS(ctx, "%s: failed to read signature (status = %d)\n",
+                __FUNCTION__, rv);
+        return false;
+    }
+
+    return intf->trust_key_in_signature_structure(ctx, scheme, signature_struct,
+                                                  *signature_struct_size);
+}
+// Validates the signature with verification key provided along with the
+// signature if the key is trusted.
+
+static bool validate_signature_with_key_in_signature_struct(
+    const struct libcr51sign_ctx* ctx, const struct libcr51sign_intf* intf,
+    enum signature_scheme scheme, uint32_t raw_signature_offset,
+    const uint8_t* digest, uint32_t digest_size)
+{
+    // pick the biggest signature struct size.
+    uint8_t signature_struct[sizeof(struct signature_rsa4096_pkcs15)];
+    uint32_t signature_struct_size = sizeof(signature_struct);
+    if (!is_key_in_signature_struct_trusted(
+            ctx, intf, scheme, raw_signature_offset, &signature_struct,
+            &signature_struct_size))
+    {
+        CPRINTS(ctx, "%s: key in signature struct is not trusted\n",
+                __FUNCTION__);
+        return false;
+    }
+    if (!intf->verify_rsa_signature_with_modulus_and_exponent)
+    {
+        CPRINTS(
+            ctx,
+            "%s: verify_rsa_signature_with_modulus_and_exponent is not supported\n",
+            __FUNCTION__);
+        return false;
+    }
+
+    switch (scheme)
+    {
+        case SIGNATURE_RSA2048_PKCS15:
+        {
+            struct signature_rsa2048_pkcs15* sig =
+                (struct signature_rsa2048_pkcs15*)signature_struct;
+            return intf->verify_rsa_signature_with_modulus_and_exponent(
+                ctx, scheme, sig->modulus, sizeof(sig->modulus), sig->exponent,
+                sig->signature, sizeof(sig->signature), digest, digest_size);
+        }
+        break;
+        case SIGNATURE_RSA3072_PKCS15:
+        {
+            struct signature_rsa3072_pkcs15* sig =
+                (struct signature_rsa3072_pkcs15*)signature_struct;
+            return intf->verify_rsa_signature_with_modulus_and_exponent(
+                ctx, scheme, sig->modulus, sizeof(sig->modulus), sig->exponent,
+                sig->signature, sizeof(sig->signature), digest, digest_size);
+        }
+        break;
+        case SIGNATURE_RSA4096_PKCS15:
+        case SIGNATURE_RSA4096_PKCS15_SHA512:
+        {
+            struct signature_rsa4096_pkcs15* sig =
+                (struct signature_rsa4096_pkcs15*)signature_struct;
+            return intf->verify_rsa_signature_with_modulus_and_exponent(
+                ctx, scheme, sig->modulus, sizeof(sig->modulus), sig->exponent,
+                sig->signature, sizeof(sig->signature), digest, digest_size);
+        }
+        break;
+        default:
+            CPRINTS(ctx, "%s: unsupported signature scheme %d\n", __FUNCTION__,
+                    scheme);
+            return false;
+    }
+}
+
 // 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".
@@ -586,6 +696,22 @@
         CPRINTS(ctx, "%s: hash_final failed (status = %d)\n", __FUNCTION__, rv);
         return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
     }
+
+    rv = get_hash_digest_size(hash_type, &digest_size);
+    if (rv != LIBCR51SIGN_SUCCESS)
+    {
+        return rv;
+    }
+
+    if (intf->trust_descriptor_hash)
+    {
+        if (intf->trust_descriptor_hash(ctx, dcrypto_digest, digest_size))
+        {
+            CPRINTS(ctx, "%s: descriptor hash trusted\n", __FUNCTION__);
+            return LIBCR51SIGN_SUCCESS;
+        }
+    }
+
     rv = get_key_size(scheme, &key_size);
     if (rv != LIBCR51SIGN_SUCCESS)
     {
@@ -599,16 +725,22 @@
                 __FUNCTION__, rv);
         return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
     }
+
+    if (validate_signature_with_key_in_signature_struct(
+            ctx, intf, scheme, raw_signature_offset, dcrypto_digest,
+            digest_size))
+    {
+        CPRINTS(ctx, "%s: verification with external key succeeded\n",
+                __FUNCTION__);
+        return LIBCR51SIGN_SUCCESS;
+    }
+
     if (!intf->verify_signature)
     {
         CPRINTS(ctx, "%s: missing verify_signature\n", __FUNCTION__);
         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)
@@ -791,10 +923,10 @@
 //@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)
+static 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;
@@ -846,12 +978,10 @@
 //                  and environment
 // @param[out] image_regions - image_region pointer to an array for the output
 //
-// TODO(aranika) return valid key
-//
 // @return nonzero on error, zero on success
 
 failure_reason libcr51sign_validate(
-    const struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
+    struct libcr51sign_ctx* ctx, struct libcr51sign_intf* intf,
     struct libcr51sign_validated_regions* image_regions)
 {
     int rv, rv_first_desc = LIBCR51SIGN_SUCCESS;
@@ -863,12 +993,14 @@
         CPRINTS(ctx, "%s: Missing context\n", __FUNCTION__);
         return LIBCR51SIGN_ERROR_INVALID_CONTEXT;
     }
-    else if (!intf)
+    if (!intf)
     {
         CPRINTS(ctx, "%s: Missing interface\n", __FUNCTION__);
         return LIBCR51SIGN_ERROR_INVALID_INTERFACE;
     }
 
+    ctx->validation_state = LIBCR51SIGN_IMAGE_INVALID;
+
     rv = scan_for_magic_8(ctx, intf, DESCRIPTOR_MAGIC, ctx->start_offset,
                           ctx->end_offset, DESCRIPTOR_ALIGNMENT,
                           &descriptor_offset);
@@ -897,6 +1029,7 @@
             }
             else if (ctx->descriptor.image_type == IMAGE_PROD)
             {
+                ctx->validation_state = LIBCR51SIGN_IMAGE_VALID;
                 // Lookup and validate payload Image MAUV against Image MAUV
                 // stored in the system after checking signature to ensure
                 // offsets and sizes are not tampered with. Also, do this after
@@ -931,6 +1064,7 @@
             }
             else
             {
+                ctx->validation_state = LIBCR51SIGN_IMAGE_VALID;
                 return rv;
             }
         }
@@ -949,8 +1083,8 @@
     // If desc validation failed for some reason then return that reason
     if (rv_first_desc != LIBCR51SIGN_SUCCESS)
         return rv_first_desc;
-    else
-        return rv;
+
+    return rv;
 }
 
 // @func to returns the libcr51sign error code as a string
@@ -1014,6 +1148,8 @@
                    "Image MAUV is present in the system";
         case LIBCR51SIGN_NO_STORED_MAUV_FOUND:
             return "Client did not find any MAUV data stored in the system";
+        case LIBCR51SIGN_ERROR_INVALID_DESCRIPTOR_BLOBS:
+            return "Invalid descriptor blobs";
         default:
             return "Unknown error";
     }
diff --git a/subprojects/libcr51sign/src/libcr51sign_support.c b/subprojects/libcr51sign/src/libcr51sign_support.c
index d0c2b96..8e8dd41 100644
--- a/subprojects/libcr51sign/src/libcr51sign_support.c
+++ b/subprojects/libcr51sign/src/libcr51sign_support.c
@@ -15,10 +15,14 @@
  */
 
 #include <libcr51sign/libcr51sign_support.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
 #include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -43,12 +47,18 @@
     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
+    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;
 }
@@ -68,12 +78,18 @@
     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
+    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;
 }
@@ -92,16 +108,24 @@
     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;
+    }
+
+    return LIBCR51SIGN_ERROR_RUNTIME_FAILURE;
 }
 
 // @func verify check that the signature is valid for given hashed data
@@ -230,6 +254,172 @@
     return rv;
 }
 
+// @func Verify RSA signature with modulus and exponent
+// @param[in]  ctx - context struct
+// @param[in]  sig_scheme - signature scheme
+// @param[in]  modulus - modulus of the RSA key, MSB (big-endian)
+// @param[in]  modulus_len - length of modulus in bytes
+// @param[in]  exponent - exponent of the RSA key
+// @param[in]  sig - signature blob
+// @param[in]  sig_len - length of signature in bytes
+// @param[in]  digest - digest to verify
+// @param[in]  digest_len - digest size
+//
+// @return true: if the signature is verified
+//         false: otherwise
+__attribute__((nonnull)) bool verify_rsa_signature_with_modulus_and_exponent(
+    const void* ctx, enum signature_scheme sig_scheme, const uint8_t* modulus,
+    int modulus_len, uint32_t exponent, const uint8_t* sig, int sig_len,
+    const uint8_t* digest, int digest_len)
+{
+    RSA* rsa = NULL;
+    BIGNUM* n = NULL;
+    BIGNUM* e = NULL;
+    int ret = 0;
+    int hash_nid = NID_undef;
+    int expected_modulus_bits = 0;
+    int expected_digest_len = 0;
+
+    CPRINTS(ctx, "%s: sig_scheme = %d\n", __FUNCTION__, sig_scheme);
+    // Determine hash NID and expected modulus size based on signature_scheme
+    switch (sig_scheme)
+    {
+        case SIGNATURE_RSA2048_PKCS15:
+            expected_modulus_bits = 2048;
+            hash_nid = NID_sha256;
+            expected_digest_len = SHA256_DIGEST_LENGTH;
+            break;
+        case SIGNATURE_RSA3072_PKCS15:
+            expected_modulus_bits = 3072;
+            hash_nid = NID_sha256;
+            expected_digest_len = SHA256_DIGEST_LENGTH;
+            break;
+        case SIGNATURE_RSA4096_PKCS15:
+            expected_modulus_bits = 4096;
+            hash_nid = NID_sha256;
+            expected_digest_len = SHA256_DIGEST_LENGTH;
+            break;
+        case SIGNATURE_RSA4096_PKCS15_SHA512:
+            expected_modulus_bits = 4096;
+            hash_nid = NID_sha512;
+            expected_digest_len = SHA512_DIGEST_LENGTH;
+            break;
+        default:
+            CPRINTS(ctx, "%s: Unsupported signature scheme.\n", __FUNCTION__);
+            return false;
+    }
+
+    // Input validation: Check digest length
+    if (digest_len != expected_digest_len)
+    {
+        CPRINTS(
+            ctx,
+            "%s: Mismatch in expected digest length (%d) and actual (%d).\n",
+            __FUNCTION__, expected_digest_len, digest_len);
+        return false;
+    }
+
+    // 1. Create a new RSA object
+    rsa = RSA_new();
+    if (rsa == NULL)
+    {
+        CPRINTS(ctx, "%s:Error creating RSA object: %s\n", __FUNCTION__,
+                ERR_error_string(ERR_get_error(), NULL));
+        goto err;
+    }
+
+    // 2. Convert raw modulus and exponent to BIGNUMs
+    n = BN_bin2bn(modulus, modulus_len, NULL);
+    if (n == NULL)
+    {
+        CPRINTS(ctx, "%s:Error converting modulus to BIGNUM: %s\n",
+                __FUNCTION__, ERR_error_string(ERR_get_error(), NULL));
+        goto err;
+    }
+
+    e = BN_new();
+    if (e == NULL)
+    {
+        CPRINTS(ctx, "%s: Error creating BIGNUM for exponent: %s\n",
+                __FUNCTION__, ERR_error_string(ERR_get_error(), NULL));
+        goto err;
+    }
+    if (!BN_set_word(e, exponent))
+    {
+        CPRINTS(ctx, "%s: Error setting exponent word: %s\n", __FUNCTION__,
+                ERR_error_string(ERR_get_error(), NULL));
+        goto err;
+    }
+
+    // Set the public key components. RSA_set0_key takes ownership of n and e.
+    if (!RSA_set0_key(rsa, n, e, NULL))
+    { // For public key, d is NULL
+        CPRINTS(ctx, "%s: Error setting RSA key components: %s\n", __FUNCTION__,
+                ERR_error_string(ERR_get_error(), NULL));
+        goto err;
+    }
+    n = NULL; // Clear pointers to prevent double-free
+    e = NULL;
+
+    if (RSA_bits(rsa) != expected_modulus_bits)
+    {
+        CPRINTS(
+            ctx,
+            "%s: Error: RSA key size (%d bits) does not match expected size for "
+            "scheme (%d bits).\n",
+            __FUNCTION__, RSA_bits(rsa), expected_modulus_bits);
+        goto err;
+    }
+
+    // Input validation: Signature length must match modulus length
+    if (sig_len != RSA_size(rsa))
+    {
+        CPRINTS(
+            ctx,
+            "%s: Error: Signature length (%d) does not match RSA key size (%d).\n",
+            __FUNCTION__, sig_len, RSA_size(rsa));
+        goto err;
+    }
+
+    // 3. Verify the signature
+    // RSA_verify handles the decryption, PKCS#1 v1.5 padding check, and hash
+    // comparison internally.
+    CPRINTS(ctx, "%s: RSA_verify\n", __FUNCTION__);
+    CPRINTS(ctx, "%s: hash_nid %d\n", __FUNCTION__, hash_nid);
+    CPRINTS(ctx, "%s: digest_len  %d, digest: \n", __FUNCTION__, digest_len);
+    for (int i = 0; i < digest_len; i++)
+    {
+        CPRINTS(ctx, "%x", digest[i]);
+    }
+    CPRINTS(ctx, "\n");
+
+    CPRINTS(ctx, "%s: sig_len %d, sig: \n", __FUNCTION__, sig_len);
+    for (int i = 0; i < sig_len; i++)
+    {
+        CPRINTS(ctx, "%x", sig[i]);
+    }
+    CPRINTS(ctx, "\n");
+
+    ret = RSA_verify(hash_nid, digest, digest_len, sig, sig_len, rsa);
+
+    if (ret == 1)
+    {
+        CPRINTS(ctx, "%s: Signature verification successful!\n", __FUNCTION__);
+    }
+    else
+    {
+        CPRINTS(ctx, "%s: Signature verification failed: %s\n", __FUNCTION__,
+                ERR_error_string(ERR_get_error(), NULL));
+    }
+
+err:
+    RSA_free(rsa); // Frees n and e if RSA_set0_key successfully took ownership
+    BN_free(n);    // Only if RSA_set0_key failed or was not called
+    BN_free(e);    // Only if RSA_set0_key failed or was not called
+    return (ret == 1);
+    (void)ctx;     // make compiler happy when CPRINTS is null statemenet
+}
+
 #ifdef __cplusplus
 } //  extern "C"
 #endif