📄 emSSH User Guide & Reference Manual
📄 emSSL User Guide & Reference Manual
📄 emUSB-Device User Guide & Reference Manual
📄 emUSB-Host User Guide & Reference Manual
📄 emVNC User Guide & Reference Manual
📄 emWeb User Guide & Reference Manual
📄 emWin User Guide & Reference Manual
📄 IoT Toolkit User Guide & Reference Manual
📄 SEGGER Assembler User Guide & Reference Manual
📄 SEGGER Compiler User Guide & Reference Manual
📄 SEGGER Linker User Guide & Reference Manual
📄 SEGGER SystemView User Guide
📄 SEGGER Online Documentation
📄 AppWizard User Guide & Reference Manual
📄 embOS Real-Time Operating System User Guide & Reference Manual
📄 embOS-Ultra Real-Time Operating System User Guide & Reference Manual
📄 emCompress-Embed User Guide & Reference Manual
📄 emCompress-LZMA User Guide & Reference Manual
📄 emCompress-ToGo User Guide & Reference Manual
📄 emCrypt User Guide & Reference Manual
📄 emDropbox User Guide & Reference Manual
📄 emFile User Guide & Reference Manual
📄 emFloat User Guide & Reference Manual
📄 emNet User Guide & Reference Manual
📄 emRun User Guide & Reference Manual
📄 emSecure-ECDSA User Guide & Reference Manual
📄 emSecure-RSA User Guide & Reference Manual

emSecure-RSA User Guide & Reference Manual

Digital signature suite.

Introduction to emSecure-RSA

This section presents an overview of emSecure-RSA, its structure, and its capabilities.

What is emSecure-RSA?

emSecure-RSA is a SEGGER software package that allows creation and verification of digital signatures. One important feature is that emSecure-RSA can make it impossible to create a clone of an embedded device by simply copying hardware and firmware.

And it can do much more, such as securing firmware updates distributed to embedded devices and authenticating licenses, serial numbers, and sensitive data.

emSecure-RSA offers 100% protection against hacking. It is not just nice to have, but in fact a must-have, not only for critical devices such as election machines, financial applications, or sensors.

Compromised devices are dangerous in several ways, not just from a commercial point of view. They hamper manufacturers’ reputation and might entail severe legal disputes. Not addressing the issue of hacking and cloning is irresponsible.

Based on asymmetric encryption algorithms with two keys, emSecure-RSA signatures cannot be forged by reverse engineering of the firmware. A secure, private key is used to generate the digital signature, whereas a second, public key is used to authenticate data by its signature. There is neither a way to get the private key from the public key, nor is it possible to generate a valid signature without the private key.

The emSecure-RSA source code has been created from scratch for embedded systems, to achieve highest portability with a small memory footprint and high performance. However, usage is not restricted to embedded systems.

With its easy usage, it takes less than one day to add and integrate emSecure-RSA into an existing product. emSecure-RSA is a very complete package, including ready-to-run tools and functionality for generation of keys and signatures.

Why should I use emSecure-RSA?

emSecure-RSA offers a fast and easy way to prevent hacking and cloning of products. Not addressing the issue of hacking and cloning would be irresponsible.

Security consideration

If you want to check the integrity of your data, for instance the firmware running on your product, you would normally include a checksum or hash value into it, generated by a CRC or SHA function. Hashes are excellent at ensuring a critical data transmission, such as a firmware download, has worked flawlessly and to verify that an image, stored in memory, has not changed. However they do not add much security, as an attacker can easily compute the hash value of modified data or images.

Digital signatures can do more. In addition to the integrity check, which is provided by hash functions, a digital signature assures the authenticity of the provider of the signed data, as only he can create a valid signature. emSecure-RSA creates digital signatures using the RSA cryptosystem that has proven robust against decades of attacks on the algorithms. For the default of 2048-bit key sizes, it is considered well beyond the capability of governments, with all their computing power and using the very latest number-theoretic methods, to recover a properly generated RSA private key before 2030, and most probably well beyond that.

emSecure-RSA can be used for two security approaches:

Features

emSecure-RSA is written in standard ANSI C and can run on virtually any CPU. Here’s a list summarizing the main features of emSecure-RSA:

We recommend keeping emSecure-RSA separate from your application files. It is good practice to keep all the source files (including the header files) together in the subdirectories of your project’s root directory as they are shipped. This practice has the advantage of being very easy to update to newer versions of emSecure-RSA by simply replacing the directories. Your application files can be stored anywhere.

Note

When updating to a newer emSecure-RSA version: as files may have been added, moved or deleted, the project directories may need to be updated accordingly.

Package content

emSecure-RSA is provided in source code and contains everything needed. The following table shows the content of the emSecure-RSA Package:

Files Description
Application emSSL sample applications for bare metal and embOS.
Config Configuration header files.
Doc emSecure-RSA documentation.
CRYPTO Shared cryptographic library source code.
SECURE emSecure-RSA implementation code.
SEGGER SEGGER software component source code used in emSecure-RSA.
Sample/Config Example emSecure-RSA configuration.
Sample/Keys Example emSecure-RSA key pairs.
Windows Supporting applications in binary and source form.

Include directories

You should make sure that the include path contains the following directories (the order of inclusion is of no importance):

Note

Always make sure that you have only one version of each file!

It is frequently a major problem when updating to a new version of emSecure-RSA if you have old files included and therefore mix different versions. If you keep emSecure-RSA in the directories as suggested (and only in these), this type of problem cannot occur. When updating to a newer version, you should be able to keep your configuration files and leave them unchanged. For safety reasons, we recommend backing up (or at least renaming) the SECURE directories before to updating.

Working with emSecure-RSA

This chapter gives some recommendations on how to use emSecure-RSA in your applications. It explains the steps of “emSecuring” a product.

Introduction

emSecure-RSA is created to be simple but powerful, and easy to integrate. It can be used in new products and even extend existing ones as emSecure-RSA is a software solution and no additional hardware is required. The code is completely written in ANSI C and can be used platform- and controller-independent.

emSecure-RSA has been created from scratch to achieve highest portability and performance with a very small memory footprint. It enables you to profit from the security of digital signatures in embedded applications, even on small single-chip microcontrollers without the need of additional hardware such as external security devices or external memory.

emSecure-RSA is a complete package. It includes ready-to-run tools to generate keys and signatures, to sign and verify data and to convert the keys and signatures into compilable formats.

The required key pairs can be generated with the included tool. The generated keys can be exported into different formats to be stored on the application code or loaded from a key file. This allows portability and exchangeability between different platforms.

Signing data, for instance firmware images, can be done with the included tool. It is also possible to integrate the signing process directly into a production application running on any PC or even on a microcontroller.

Once a signature is generated, the signed data can be verified by its signature in an embedded application or on an external application communicating with the device. Verifying data takes less than 40 ms on a Cortex-M4, running at 200 MHz, which is not significantly more time for a bootloader to start a firmware.

emSecure-RSA includes all required source code to integrate signature generation directly into your production process and data verification into your application or firmware.

emSecure-RSA incorporates proven security algorithms as proposed by NIST. The algorithms are proven to be cryptographically strong and can provide a maximum of security to your applications.

emSecure-RSA is licensed in the same way as other SEGGER middleware products and not covered by an open-source or required-attribution license. It can be integrated in any commercial or proprietary product without the obligation to disclose the combined source. It can be used royalty-free in your product.

Anti-hacking

Authentication of firmware

To make sure only authorized firmware images are run on a product the firmware image will be signed with emSecure-RSA. To do this an emSecure-RSA key pair is generated one time.

The private key will be included in the production process of the firmware. Once a firmware is created and ready to be shipped or included into a product it will be signed with this private key. The signature will be transferred and stored in the product alongside the firmware.

Signing firmware

The public key will be included in the bootloader of the product, which manages firmware updates and starts the firmware.

On a firmware update and when starting the product, the bootloader will verify the firmware by its signature. If they match, the firmware is started, otherwise the application will stay in the bootloader or even erase the firmware.

Verifying firmware

Anti-cloning

Authentication of hardware

To make sure a product cannot be re-produced by non-authorized manufacturers, by simply copying the hardware, emSecure-RSA will be used to sign each genuine product unit.

First an emSecure-RSA key pair is generated one time. This is likely done at the production site.

Signing firmware for a specific device

The private key will be included in the production process of the product. At the end of the production process, after the unit is assembled and tested, some hardware-specific, fixed, and unique data, like the unique id of the microcontroller is read from the unit. This data is signed by emSecure-RSA with the private key and the signature is written back to the unit into an OTP area or a specified location on memory.

The public key will be included in the firmware which will run on the product. When the firmware is running it will read the unique data from the unit and verify it with the signature. When the signature does not match, for example, when it was simply copied to a counterfeit unit with other unique data, the firmware will refuse to run.

Verifying firmware for a specific device

Additional measures to keep the system secure

When it comes to the degree of security emSecure-RSA offers, there is an easy answer: It is unbreakable because no one can generate a valid signature without knowledge of the private key.

Putting enough effort into getting the bootloader or firmware image, disassembling and analyzing it and modifying the application to bypass the security measures, hackers might be able to clone a product or use alternative firmware images. However this will only work until a firmware update is done.

There are additional ways to increase overall system security:

Included applications

This chapter describes the applications which are part of the emSecure-RSA package. The executables can be found at /Windows/SECURE. The source code of the basic utilities is included.

emSecure-RSA includes all basic applications required for securing a product. The applications’ source-code is included and provides an easy to use starting point for modifications and integration into other applications.

The source code for the key generator is an add-on: please contact SEGGER for more information.

emSecure-RSA Key Generator

emSecure-RSA KeyGen generates a public and a private key. The generation parameters can be set via command line options, by default a random 2048 bit key is generated. The keys are saved in a common key file format and can be published and exchanged.

Usage

emKeyGenRSA.exe [<Options>]

Command line options

emSecure-RSA KeyGen accepts the following command line options.

Option Description
-h Print usage information and available command line options.
-q Operate silently and do not print log output.
-v Increase verbosity of log output.
-k string Set the key file prefix to string. Default is “emSecure”.
-l n Set the modulus length to n bits. Default is 2048. For modulus lengths that other than 2048 and 3072, -nf is required.
-seed n Set the initial seed for random number generation to n.
-pw string Generate the initial seed from the pass phrase string.
-f Generate proven primes for the key pair according to FIPS algorithms. This option is default and recommended to be used with a key length of 2048 bits.
-nf Generate probabilistic primes for the key pair.
-pem Write key files in PEM format.

emSecure-RSA Sign

emSignyRSA digitally signs file content, usually the data to be secured, with a given (private) key file and creates a signature file.

Usage

emSignRSA.exe [<Options>] <InputFile>

Command line options

Option Description
-h Print usage information and available command line options.
-q Operate silently and do not print log output.
-v Increase verbosity of log output.
-k string Set the key file prefix to string. Default is “emSecure”.
-sha1 Select SHA-1 as the hash function when generating the digest to be signed.
-sha256 Select SHA-256 as the hash function when generating the digest to be signed.
-sha512 Select SHA-256 as the hash function when generating the digest to be signed.
-pss Select RSASSA-PSS as the signature scheme to use when creating the signature.
-pkcs Select RSASSA-PKCS1-v1.5 as the signature scheme to use when creating the signature.

emSecure-RSA Verify

emVerifyRSA accepts a signature file and verifies if the corresponding data file matches the signature.

Usage

emVerifyRSA.exe [<Options>] <InputFile>

Command line options

Option Description
-h Print usage information and available command line options.
-q Operate silently and do not print log output.
-v Increase verbosity of log output.
-b Signature file is pure binary, do not probe the signature file to discover its format.
-s n Set the salt length to n bytes. For nonempty salts on signing, correct verification requires that only the salt length be provided to the signature verifier, not the original salt content.
-k string Set the key file prefix to string. Default is “emSecure”.
-sha1 Select SHA-1 as the hash function when computing the message digest.
-sha256 Select SHA-256 as the hash function when computing the message digest.
-sha512 Select SHA-512 as the hash function when computing the message digest.
-pss Select RSASSA-PSS as the signature scheme to use when verifying the signature.
-pkcs Select RSASSA-PKCS1-v1.5 as the signature scheme to use when verifying the signature.

emSecure-RSA Print Key

emPrintKeyRSA exports key and signature files into a format suitable for compilation by a standard C compiler. The output can be linked into your application, so there is no need to load them from a file at runtime. This is especially useful for embedded applications.

Usage

emPrintKeyRSA.exe [<Options>] <Input-File>

Command line options

emSecure-RSA PrintKey accepts the following command line options.

Option Description
-v Increase verbosity of log output.
-x Define objects with external linkage.
-p string Prefix object names with string. Default is “_”.

emSecure-RSA walkthrough

This section will walk you through generating keys, installing those in an application, and then signing some data and verifying that the signature operation succeeded.

Generating the keys

Before signing anything, you must generate a set of keys that can sign data and verify the generated signatures. The tool emKeyGenRSA will construct the keys for you: they are generated using secure random numbers such that they are strong.

To use emKeyGenRSA you must choose the key length to use for RSA. The default key length is 2048 bit, which is considered to be secure.

emSecure-RSA keys can be generated randomly or from a pass-phrase. Generating them from a pass-phrase allows you to always re-generate the same keys.

In this example we will choose a 2048 bit key generated from the pass-phrase “SEGGER - The Embedded Experts”

Generating the keys is a matter of running emKeyGenRSA:

C:> emKeyGenRSA -l 2048 -pw "SEGGER - The Embedded Experts"

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA KeyGen V2.38 compiled May 17 2018 10:43:55

Generating proven prime key pair with public modulus of 2048 bits
Public encryption exponent is set to 65537
Initial seed is 0xADBE961296F573AD2FA65468E1A8837D
Checking keys are consistent: OK
Writing public key file emSecure.pub.
Writing private key file emSecure.prv.

C:> _

The two files that are written contain the public key and the private key, together making a matched key pair. The private key is required when signing some data and must be kept private and secure. The public key is required when verifying some signed data and can be distributed without concern for privacy.

Testing the keys

You can test out the keys by signing and verifying a small text file:

C:> dir >test.txt
C:> emSignRSA test.txt

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Sign V2.38 compiled May 17 2018 10:43:55

Loading private key from emSecure.prv
Probing file to determine type of key: RSA key detected
  Public modulus is 2048 bits
Loading content from test.txt
  Loaded content is 790 bytes
Writing signature file test.txt.sig

C:> _

Once signed, you can verify the signature:

C:> emVerifyRSA test.txt

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Verify V2.38 compiled May 17 2018 10:43:55

Loading public key from emSecure.pub
Probing file to determine type of key: RSA key detected
  Public modulus is 2048 bits
Loading RSA signature from test.txt.sig
  Loading content from test.txt
  Loaded content is 790 bytes
Signature OK.

C:> _

If you tamper with the signature or alter the original file, even by one bit, the alteration is detected and the signature is not verified:

C:> edit test.txt
C:> emVerifyRSA test.txt

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Verify V2.38 compiled May 17 2018 10:43:55

Loading public key from emSecure.pub
Probing file to determine type of key: RSA key detected
  Public modulus is 2048 bits
Loading RSA signature from test.txt.sig
  Loading content from test.txt
  Loaded content is 790 bytes
Signature NOT VERIFIED!

C:> _

Signing and verifying in your application

Now that you have a pair of keys, it’s time to integrate them into your program. You can do that by using emPrintKeyRSA which converts the textual form of the key into something that a C program can use.

First we convert the private and public keys into C declarations:

C:> emPrintKeyRSA SECURE_RSA_Expert_Key.prv -p _SECURE_RSA_PrivateKey_Expert \
      >SECURE_RSA_PrivateKey_Expert.h

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA PrintKey V2.38 compiled May 17 2018 10:43:55

Probing file to determine type of key

C:> emPrintKeyRSA SECURE_RSA_Expert_Key.pub -p _SECURE_RSA_PublicKey_Expert \
      >SECURE_RSA_PublicKey_Expert.h

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA PrintKey V2.38 compiled May 17 2017 10:43:55

Probing file to determine type of key

C:> _

The generated output contains declarations in a format suitable for direct inclusion into C program. The -p option sets the prefix for the names of the generated C identifiers.

Now we have the keys, we can write a program that uses the private key to sign an message and a public key to verify it:

/*********************************************************************
*                     SEGGER Microcontroller GmbH                    *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*       (c) 2003 - 2019  SEGGER Microcontroller GmbH                 *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File        : CRYPTO_RSA_Example1.c
Purpose     : Sign and verify a message.

Additional information:
  Preparations:
    None.

  Expected behavior:
    Signs and verifies a message using RSA.
    For more detailed information see the emSecure RSA manual.

  Sample output:
    Signed message...SUCCESS!
    Verified message is correctly signed...SUCCESS!

*/

/*********************************************************************
*
*       #include section
*
**********************************************************************
*/

#include "SECURE_RSA.h"
#include "SECURE_RSA_PrivateKey_Expert.h"
#include "SECURE_RSA_PublicKey_Expert.h"
#include <stdio.h>

/*********************************************************************
*
*       Static const data
*
**********************************************************************
*/

static const U8 _aMessage[] = { "This is a message, sign me." };

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

static U8 _aSignature[SECURE_RSA_MAX_KEY_LENGTH / 8];

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/

int main(void) {
  int SigLen;
  int Status;
  //
  SigLen = SECURE_RSA_Sign(&_SECURE_RSA_PrivateKey_Expert,
                           0, 0,
                           &_aMessage[0], sizeof(_aMessage),
                           &_aSignature[0], sizeof(_aSignature));
  if (SigLen > 0) {
    printf("Signed message...SUCCESS!\n");
    Status = SECURE_RSA_Verify(&_SECURE_RSA_PublicKey_Expert,
                               0, 0,
                               &_aMessage[0], sizeof(_aMessage),
                               &_aSignature[0], SigLen);
    if (Status > 0) {
      printf("Verified message is correctly signed...SUCCESS!\n");
    } else {
      printf("Correctly signed message did not verify...ERROR!\n");
    }
  } else {
    printf("Failed to sign message...ERROR!\n");
  }
  return 0;
}

/*************************** End of file ****************************/

The private key is defined as a constant SECURE_RSA_PrivateKey_Expert in SECURE_RSA_PrivateKey_Expert.h The public key is defined as a constant SECURE_RSA_PublicKey_Expert in SECURE_RSA_PublicKey_Expert.h

If you compile and run this application, you will see this output:

C:> SECURE_RSA_Example1
Signed message...SUCCESS!
Verified message is correctly signed...SUCCESS!

C:> _

If you tamper with the signature or the message, the signature is detected as invalid. For instance, altering the program like this…

SigLen = SECURE_RSA_Sign(&_SECURE_RSA_PrivateKey_Expert,
                         0, 0,
                         &_aMessage[0], sizeof(_aMessage),
                         &_aSignature[0], sizeof(_aSignature));
if (SigLen > 0) {
  printf("Signed message...SUCCESS!\n");
  _aMessage[0]++;
  Status = SECURE_RSA_Verify(&_SECURE_RSA_PublicKey_Expert,

…causes verification to fail:

C:> SECURE_RSA_Example1
Signed message...SUCCESS!
Correctly signed message did not verify...ERROR!

C:> _

Incremental sign and verify

The previous example shows how to sign and verify a complete message. However, it may well be that the message to sign or verify is not able to entirely fit into the microcontroller’s memory. If this is the case, emSecure-RSA offers the ability to compute the message signature and verify that signature incrementally.

Incrementally signing and verifying a message is very straightforward:

The following example shows how to do this:

/*********************************************************************
*                     SEGGER Microcontroller GmbH                    *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*       (c) 2003 - 2019  SEGGER Microcontroller GmbH                 *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File        : CRYPTO_RSA_Example2.c
Purpose     : Incrementally sign and verify a message.

Additional information:
  Preparations:
    None.

  Expected behavior:
    Incrementally signs and verifies a message split over several
    parts using RSA.
    For more detailed information see the emSecure RSA manual.

  Sample output:
    Signed message...SUCCESS!
    Verified message is correctly signed...SUCCESS!

*/

/*********************************************************************
*
*       #include section
*
**********************************************************************
*/

#include "SECURE_RSA.h"
#include "SECURE_RSA_PrivateKey_Expert.h"
#include "SECURE_RSA_PublicKey_Expert.h"
#include <stdio.h>

/*********************************************************************
*
*       Static const data
*
**********************************************************************
*/

static const U8 _aMessagePart1[] = { "This is a message, " };
static const U8 _aMessagePart2[] = { "sign me." };

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

static U8 _aSignature[SECURE_RSA_MAX_KEY_LENGTH / 8];

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

static int _Sign(void) {
  SECURE_RSA_HASH_CONTEXT Context;
  //
  // Sign message incrementally.
  //
  SECURE_RSA_HASH_Init(&Context);
  SECURE_RSA_HASH_Add (&Context, &_aMessagePart1[0], sizeof(_aMessagePart1));
  SECURE_RSA_HASH_Add (&Context, &_aMessagePart2[0], sizeof(_aMessagePart2));
  //
  return SECURE_RSA_HASH_Sign(&Context,
                              &_SECURE_RSA_PrivateKey_Expert,
                              NULL, 0,
                              &_aSignature[0], sizeof(_aSignature));
}

static int _Verify(int SigLen) {
  SECURE_RSA_HASH_CONTEXT Context;
  //
  // Verify message incrementally.
  //
  SECURE_RSA_HASH_Init(&Context);
  SECURE_RSA_HASH_Add (&Context, &_aMessagePart1[0], sizeof(_aMessagePart1));
  SECURE_RSA_HASH_Add (&Context, &_aMessagePart2[0], sizeof(_aMessagePart2));
  //
  return SECURE_RSA_HASH_Verify(&Context,
                                &_SECURE_RSA_PublicKey_Expert,
                                NULL, 0,
                                &_aSignature[0], SigLen);
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/

int main(void) {
  int SigLen;
  //
  SigLen = _Sign();
  if (SigLen > 0) {
    printf("Signed message...SUCCESS!\n");
    if (_Verify(SigLen) > 0) {
      printf("Verified message is correctly signed...SUCCESS!\n");
    } else {
      printf("Correctly signed message did not verify...ERROR!\n");
    }
  } else {
    printf("Failed to sign message...ERROR!\n");
  }
  return 0;
}

/*************************** End of file ****************************/

Using OpenSSL with emSecure-RSA

emSecure-RSA will accept RSA public and private keys that are stored in Privacy Enhanced Mail (PEM) format and validate RSA signatures in PKCS#1 v1.5 format. This section describes how to combine emSecure-RSA and OpenSSL into a sign and verify workflow.

What to expect with OpenSSL

emSecure-RSA supports two signature schemes, RSASSA-PSS and RSASSA-PKCS1-v1.5. The most recent releases of OpenSSL that ship with Mac OS X and most Linux hosts only support older PKCS #1 signing. Therefore, to use signatures that are compatible with both emSecure-RSA and OpenSSL, you must select the RSASSA-PKCS1-v1.5 signature scheme if using the included applications, and you must select that signature scheme when configuring emSecure-RSA on the target equipment.

Using OpenSSL to generate keys

emSecure-RSA will accept unencrypted PEM private keys to sign and PEM public keys to verify.

The following command generates a PEM-formatted RSA keypair whose modulus size is 2048 bits:

$ openssl genrsa -out private.pem 2048
Generating RSA private key, 2048 bit long modulus
..+++
..........+++
e is 65537 (0x10001)
$ _

The private key file contains both private and public key parts. The following command extracts the public key used for verification of signatures, eliminating the private key information:

$ openssl rsa -in private.pem -out public.pem -outform PEM -pubout
writing RSA key
$ _

These keys are automatically detected as PEM format when provided to emSecure-RSA utilities.

Signing and verifying using PEM keys

Signing

emSecure-RSA probes the provided key file to determine its format, which can be a native emSecure-RSA key file or a PEM-formatted key file generated, for instance, by OpenSSL. Because the aim of this section is to demonstrate how to use emSecure-RSA with OpenSSL, we must be mindful to select the RSASSA-PKCS1-v1.5 scheme:

C:> echo "Hello, world" >test.txt
C:> emSignRSA -k private.pem -pkcs test.txt

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Sign V2.38 compiled May 17 2017 10:43:55

Loading private key from private.pem
  Probing file: Key file accepted
  Modulus length is 2048 bits
Loading content from test.txt
  Loaded content is 17 bytes
Writing signature file test.txt.sig

C:> _

Verifying

emSecure-RSA probes the provided key file to determine its format, which can be a native emSecure-RSA key file or a PEM-formatted key file. When verifying, the signature will only correctly verify if the same signature scheme is used when signing and verifying:

C:> emVerifyRSA -k public.pem -pkcs test.txt

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Verify V2.38 compiled May 17 2017 10:43:55

Loading public key from public.pem
 Probing file: Key file accepted
  Modulus length is 2048 bits
Loading signature from test.txt.sig
Loading content from test.txt
  Loaded content is 17 bytes
Signature OK.

C:> _

Replicating the signature process with OpenSSL

The preceding section signed a candidate file with an OpenSSL-generated private key and verified the signature of the file with the OpenSSL-generated public key. We now need to show that we can verify an OpenSSL computed signature with emSecure-RSA on the host and on the target.

Using a modern OpenSSL

We start by computing the message digest of the input file using OpenSSL’s dgst command:

$ openssl dgst -sha1 -binary test.txt >test.sha1
$ _

This creates a 20-byte SHA-1 message digest in binary format in test.sha1.

$ ls -l test1.sha1
-rw-r--r--  1 plc  staff  20  1 Feb 19:57 test.sha1
$ hexdump -C test.sha1
00000000  cc fb dc d2 24 d8 60 a8  d7 b5 af 4f 4f 2f 79 ad  |....$.`....OO/y.|
00000010  c2 0d 1a d9                                       |....|
00000014
$ _

If your installation of OpenSSL understands the pkeyutl command (e.g. as shipped with Ubuntu Linux 14.04 and later), you can sign the file in RSASSA-PKCS1-v1.5 format with a single command:

$ openssl pkeyutl -sign -in test.sha1 -inkey private.pem -out test.txt.sig \
    -pkeyopt digest:sha1
$ _

Using an older OpenSSL

If your installation open OpenSSL does not understand the pkeyutl command (e.g. as shipped with OS X up to and including El Capitan), some additional steps are needed to manually sign the file.

In this case and before signing, we need to DER-encode the plain digest to conform with RSASSA-PKCS1-v1.5’s encoding algorithm, EMSA-PKCS1-v1.5, to form the encoded message, EM. For a SHA-1 signature, this means we need to prefix the message with some magic:

$ printf "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14" >prefix
$ cat prefix test.sha1 >test.em
$ hexdump -C test.em
00000000  30 21 30 09 06 05 2b 0e  03 02 1a 05 00 04 14 cc  |0!0...+.........|
00000010  fb dc d2 24 d8 60 a8 d7  b5 af 4f 4f 2f 79 ad c2  |...$.`....OO/y..|
00000020  0d 1a d9                                          |...|
00000023
$ _

Now we sign this correctly-formatted data with our private key, applying PKCS #1 padding, using OpenSSL’s rsautl command:

$ openssl rsautl -sign -inkey private.pem -in test.em >test.txt.sig
$ _

We now have a binary signature in test.txt.sig that we can instruct emSecure-RSA to verify:

C:> emVerifyRSA.exe -b -pkcs -k public.pem test.txt

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Verify V2.38 compiled May 17 2017 10:43:55

Loading public key from public.pem
 Probing file: Key file accepted
  Modulus length is 2048 bits
Loading binary signature from test.txt.sig
Loading content from test.txt
  Loaded content is 17 bytes
Signature OK.

C:> _

If you need to use SHA-256, you can wrap the digest according to section 9.2 of PKCS #1 v2.2. In practice, this simply means that you replace the prefix above with the prefix constructed for a SHA-256 message digest algorithm.

API reference

This section describes the public API for emSecure-RSA. Any functions or data structures that are not described here but are exposed through inclusion of the SECURE.h header file must be considered private and subject to change.

Preprocessor symbols

Version number

Description

Symbol expands to a number that identifies the specific emSecure-RSA release.

Definition

#define SECURE_RSA_VERSION    24600

Symbols

Definition Description
SECURE_RSA_VERSION Format is “Mmmrr” so, for example, 23600 corresponds to version 2.36.

API functions

Function Description
Initialization
SECURE_RSA_Init() Initialize emSecure-RSA.
Sign and verify an entire message
SECURE_RSA_Sign() Signs message.
SECURE_RSA_SignEx() Signs message, external allocation.
SECURE_RSA_Verify() Verify message.
SECURE_RSA_VerifyEx() Verify message, external allocation.
Sign and verify a precomputed message digest
SECURE_RSA_SignDigest() Sign message digest.
SECURE_RSA_VerifyDigest() Verify message digest.
Incrementally sign and verify a message
SECURE_RSA_HASH_Init() Initialize, incremental sign or verify.
SECURE_RSA_HASH_Add() Add message data, incremental.
SECURE_RSA_HASH_Sign() Sign message, incremental.
SECURE_RSA_HASH_Verify() Verify message, incremental.
Key setup functions
SECURE_RSA_InitPrivateKey() Initialize private key.
SECURE_RSA_InitPublicKey() Initialize public key.
Version information
SECURE_RSA_GetCopyrightText() Get copyright as printable string.
SECURE_RSA_GetVersionText() Get version as printable string.

SECURE_RSA_Init()

Description

Initialize emSecure-RSA.

Prototype

void SECURE_RSA_Init(void);

Additional information

If not already installed as part of CRYPTO initialization, install the software-implemented hash function selected by emSecure-RSA configuration and basic fast modular exponentiation.

SECURE_RSA_Sign()

Description

Signs message.

Prototype

int SECURE_RSA_Sign(const SECURE_RSA_PRIVATE_KEY * pPrivate,
                    const U8                     * pSalt,
                          int                      SaltLen,
                    const U8                     * pMessage,
                          int                      MessageLen,
                          U8                     * pSignature,
                          int                      SignatureLen);

Parameters

Parameter Description
pPrivate Pointer to private key to sign with.
pSalt Pointer to salt value to embed.
SaltLen Octet length of the salt.
pMessage Pointer to message to sign.
MessageLen Octet length of message to sign.
pSignature Pointer to object that receives the generated signature.
SignatureLen Octet length of the signature.

Return value

≤ 0 Signature failure (signature buffer too small).
> 0 Success, number of bytes written to the the signature buffer that constitutes the signature.

Example

static const U8 acData[] = {
                            // ... Data here ...
                           };
//
static const U8 acSalt[] = "SEGGER - The embedded Experts";
static       U8 acSig[512];
//
int _Sign(void) {
  int r;
  r = SECURE_RSA_Sign(&GLOBAL_PrivateKey,
                      acSalt, 30,
                      acData, sizeof(acData),
                      acSig,  sizeof(acSig));
  return r;
}

SECURE_RSA_SignEx()

Description

Signs message, external allocation.

Prototype

int SECURE_RSA_SignEx(const SECURE_RSA_PRIVATE_KEY * pPrivate,
                      const U8                     * pSalt,
                            int                      SaltLen,
                      const U8                     * pMessage,
                            int                      MessageLen,
                            U8                     * pSignature,
                            int                      SignatureLen,
                            SEGGER_MEM_CONTEXT     * pMem);

Parameters

Parameter Description
pPrivate Pointer to private key to sign with.
pSalt Pointer to salt value to embed.
SaltLen Octet length of the salt.
pMessage Pointer to message to sign.
MessageLen Octet length of message to sign.
pSignature Pointer to object that receives the generated signature.
SignatureLen Octet length of the signature.
pMem Allocator to use for temporary storage.

Return value

≤ 0 Signature failure (signature buffer too small).
> 0 Success, number of bytes written to the the signature buffer that constitutes the signature.

Additional information

This is functionally identical to SECURE_RSA_Sign() but rather than using stack-allocated temporary storage, storage is provided by the initialized memory context.

SECURE_RSA_Verify()

Description

Verify message.

Prototype

int SECURE_RSA_Verify(const SECURE_RSA_PUBLIC_KEY * pPublic,
                            U8                    * pSalt,
                            int                     SaltLen,
                      const U8                    * pMessage,
                            int                     MessageLen,
                      const U8                    * pSignature,
                            int                     SignatureLen);

Parameters

Parameter Description
pPublic Public key used to verify the message.
pSalt Pointer to buffer that receives the recovered salt. If pSalt is null, the salt is not recovered, but a correct SaltLen must still be provided.
SaltLen Octet length of the original salt.
pMessage Pointer to message to verify.
MessageLen Octet length of message.
pSignature Pointer to signature to verify.
SignatureLen Octet length of the signature.

Return value

≤ 0 Verification failure (incorrect structure of encoded message, signature does not match).
> 0 Correct verification of the signature.

Example

static const U8 acData[] = {
                            // ... Data here ...
                           };
static const U8 acSig[]  = {
                            // ... Signature here ...
                           };
static       U8 acSalt[256];
//
int _Verify(void) {
  int r;
  r = SECURE_RSA_Verify(&GLOBAL_PublicKey, 
                        acSalt, 30,
                        acData, sizeof(acData),
                        acSig, sizeof(acSig));
  return r;
}

SECURE_RSA_VerifyEx()

Description

Verify message, external allocation.

Prototype

int SECURE_RSA_VerifyEx(const SECURE_RSA_PUBLIC_KEY * pPublic,
                              U8                    * pSalt,
                              int                     SaltLen,
                        const U8                    * pMessage,
                              int                     MessageLen,
                        const U8                    * pSignature,
                              int                     SignatureLen,
                              SEGGER_MEM_CONTEXT    * pMem);

Parameters

Parameter Description
pPublic Pointer to public key used to verify the message.
pSalt Pointer to object that receives the recovered salt. If pSalt is null, the salt is not recovered, but SaltByteCnt must still be given.
SaltLen Octet length of the original salt.
pMessage Pointer to message to verify.
MessageLen Octet length of the message.
pSignature Pointer to signature to verify.
SignatureLen Octet length of the signature.
pMem Allocator to use for temporary storage.

Return value

≤ 0 Verification failure (incorrect structure of encoded message, signature does not match).
> 0 Correct verification of the signature.

Additional information

This is functionally identical to SECURE_RSA_Verify() but rather than using stack-allocated temporary storage, storage is provided by the initialized memory context.

SECURE_RSA_HASH_Add()

Description

Add message data, incremental.

Prototype

void SECURE_RSA_HASH_Add(      SECURE_RSA_HASH_CONTEXT * pHash,
                         const void                    * pData,
                               unsigned                  DataLen);

Parameters

Parameter Description
pHash Pointer to hash context.
pData Pointer to message fragment to add to hash.
DataLen Octet length of the message fragment.

See also

Incremental sign and verify

SECURE_RSA_HASH_Init()

Description

Initialize, incremental sign or verify.

Prototype

void SECURE_RSA_HASH_Init(SECURE_RSA_HASH_CONTEXT * pHash);

Parameters

Parameter Description
pHash Hash context covering message.

See also

Incremental sign and verify

SECURE_RSA_HASH_Sign()

Description

Sign message, incremental.

Prototype

int SECURE_RSA_HASH_Sign(      SECURE_RSA_HASH_CONTEXT * pHash,
                         const SECURE_RSA_PRIVATE_KEY  * pPrivate,
                         const U8                      * pSalt,
                               int                       SaltLen,
                               U8                      * pSignature,
                               int                       SignatureLen);

Parameters

Parameter Description
pHash Hash context covering message.
pPrivate Pointer to private key to sign with.
pSalt Pointer to salt octet string.
SaltLen Octet length of the salt.
pSignature Pointer to object that receives the generated signature.
SignatureLen Octet length of the signature.

Return value

≤ 0 Signature failure (signature buffer too small).
> 0 Success, number of bytes written to the the signature buffer that constitute the signature.

Additional information

The signature object have a capacity of at least the octet length of the modulus. Hence, for a 1024-bit prime, the signature object must be at lease 128 bytes in length.

See also

Incremental sign and verify

SECURE_RSA_HASH_Verify()

Description

Verify message, incremental.

Prototype

int SECURE_RSA_HASH_Verify(      SECURE_RSA_HASH_CONTEXT * pHash,
                           const SECURE_RSA_PUBLIC_KEY   * pPublic,
                                 U8                      * pSalt,
                                 int                       SaltLen,
                           const U8                      * pSignature,
                                 int                       SignatureLen);

Parameters

Parameter Description
pHash Hash context covering message.
pPublic Public key used to verify the message.
pSalt Pointer to buffer for recovered salt. If pSalt is null, the salt is not recovered, but SaltLen must still be given.
SaltLen Length of the original salt.
pSignature Pointer to signature to verify.
SignatureLen Octet length of the signature.

Return value

≤ 0 Verification failure (incorrect structure of encoded message, signature does not match).
> 0 Correct verification of the signature.

See also

Incremental sign and verify

SECURE_RSA_SignDigest()

Description

Sign message digest.

Prototype

int SECURE_RSA_SignDigest(const SECURE_RSA_PRIVATE_KEY * pPrivate,
                          const U8                     * pSalt,
                                int                      SaltLen,
                          const U8                     * pDigest,
                                U8                     * pSignature,
                                int                      SignatureLen);

Parameters

Parameter Description
pPrivate Pointer to private key to sign with.
pSalt Pointer to salt value to embed.
SaltLen Octet length of the salt.
pDigest Pointer to octet string that contains the hash of the message to sign.
pSignature Pointer to object that receives the generated signature.
SignatureLen Octet length of the signature.

Return value

≤ 0 Signature failure (signature buffer too small).
> 0 Success, number of bytes written to the the signature buffer that constitute the signature.

SECURE_RSA_VerifyDigest()

Description

Verify message digest.

Prototype

int SECURE_RSA_VerifyDigest(const SECURE_RSA_PUBLIC_KEY * pPublic,
                                  U8                    * pSalt,
                                  int                     SaltLen,
                            const U8                    * pDigest,
                            const U8                    * pSignature,
                                  int                     SignatureLen);

Parameters

Parameter Description
pPublic Public key used to verify the message.
pSalt Pointer to buffer for recovered salt. If pSalt is null, the salt is not recovered, but SaltLen must still be given.
SaltLen Octet length of the original salt.
pDigest Hash of original message.
pSignature Pointer to signature to verify.
SignatureLen Octet length of the signature.

Return value

≤ 0 Verification failure (incorrect structure of encoded message, signature does not match).
> 0 Correct verification of the signature.

SECURE_RSA_InitPrivateKey()

Description

Initialize private key.

Prototype

void SECURE_RSA_InitPrivateKey(      SECURE_RSA_PRIVATE_KEY   * pPrivate,
                               const SECURE_RSA_KEY_PARAMETER * pParamDP,
                               const SECURE_RSA_KEY_PARAMETER * pParamDQ,
                               const SECURE_RSA_KEY_PARAMETER * pParamP,
                               const SECURE_RSA_KEY_PARAMETER * pParamQ,
                               const SECURE_RSA_KEY_PARAMETER * pParamQInv);

Parameters

Parameter Description
pPrivate Pointer to private key to be initialized.
pParamDP Pointer to public key parameter DP.
pParamDQ Pointer to public key parameter DQ.
pParamP Pointer to public key parameter P.
pParamQ Pointer to public key parameter Q.
pParamQInv Pointer to public key parameter QInv.

SECURE_RSA_InitPublicKey()

Description

Initialize public key.

Prototype

void SECURE_RSA_InitPublicKey(      SECURE_RSA_PUBLIC_KEY    * pPublic,
                              const SECURE_RSA_KEY_PARAMETER * pParamE,
                              const SECURE_RSA_KEY_PARAMETER * pParamN);

Parameters

Parameter Description
pPublic Pointer to public key to be initialized.
pParamE Pointer to public key parameter E.
pParamN Pointer to public key parameter N.

SECURE_RSA_GetCopyrightText()

Description

Get copyright as printable string.

Prototype

char *SECURE_RSA_GetCopyrightText(void);

Return value

Zero-terminated copyright string.

SECURE_RSA_GetVersionText()

Description

Get version as printable string.

Prototype

char *SECURE_RSA_GetVersionText(void);

Return value

Zero-terminated version string.

Configuring emSecure-RSA

emSecure-RSA can be configured using preprocessor symbols. All compile-time configuration symbols are preconfigured with valid values which match the requirements of most applications.

The cryptography modules (prefixed CRYPTO_), and SEGGER modules (prefixed SEGGER_) are shared with other SEGGER products, for instance emSSL, and should be configured to match all modules which are used in the same application.

Algorithm parameters

Key length

Default

#define SECURE_RSA_MAX_KEY_LENGTH     2048

Override

To define a non-default value, define this symbol in SECURE_RSA_Conf.h.

Description

Configure the maximum length of keys. This preprocessor symbol is used to reserve enough memory when signing or verifying a message.

Hash function

Default

#define SECURE_HASH_FUNCTION     SHA1

Override

To define a non-default value, define this symbol in SECURE_RSA_Conf.h.

Description

Configure the hash function to use. This preprocessor symbol SECURE_HASH_FUNCTION, if defined, must be set to one of the following:

Value Description
SHA1 Use SHA-1 with 20-byte digest as the underlying hash function.
SHA256 Use SHA-256 with a 32-byte digest as the underlying hash function.
SHA512 Use SHA-512 with a 64-byte digest as the underlying hash function.

Signature scheme

Default

#define SECURE_SIGNATURE_SCHEME     PSS

Override

To define a non-default value, define this symbol in SECURE_RSA_Conf.h.

Description

Configure the signature scheme use. This preprocessor symbol SECURE_SIGNATURE_SCHEME, if defined, must be set to one of the following:

Value Description
PSS Use RSASSA-PSS as the signature scheme.
PKCS1 Use RSASSA-PKCS1-v1.5 as the signature scheme.

Crytographic components

The following definitions must be set to configure the underlying cryptographic algorithm library, emCrypt.

The relevant sections of the emCrypt documentation are included here.

Multiprecision integers

Default

#define CRYPTO_MPI_BITS_PER_LIMB     32

Override

To define a non-default value, define this symbol in CRYPTO_Conf.h.

Description

This preprocessor symbol configures the number of bits per limb for multiprecision integer algorithms. The default of 32 matches 32-bit targets well, such as ARM and PIC32. In general, it is best to set the number of bits per limb to the number of bits in the standard int or unsigned type used by the target compiler.

Supported configurations are:

Hash algorithms

The following definitions can be set for the hash components in order to balance code size and performance.

SHA-1

Default

#define CRYPTO_CONFIG_SHA1_OPTIMIZE     0

Override

To define a non-default value, define this symbol in CRYPTO_Conf.h.

Description

Set this preprocessor symbol to zero to optimize the SHA-1 hash functions for size rather than for speed. When optimized for speed, the SHA-1 function is open coded and faster, but is significantly larger.

Profile

The following table shows required context size, lookup table (LUT) size, and code size in kilobytes for each configuration value. All values are approximate and for a Cortex-M4 processor.

Setting Context size LUT LUT size Code size Total size
0 0.16 KB - - 0.6 KB 0.6 KB
1 0.16 KB - - 3.6 KB 3.6 KB
SHA-256

Default

#define CRYPTO_CONFIG_SHA256_OPTIMIZE     0

Override

To define a non-default value, define this symbol in CRYPTO_Conf.h.

Description

Set this preprocessor symbol to zero to optimize the SHA-256 hash functions for size rather than for speed. When optimized for speed, the SHA-256 function is open coded and faster, but is significantly larger.

Profile

The following table shows required context size, lookup table (LUT) size, and code size in kilobytes for each configuration value. All values are approximate and for a Cortex-M3 processor.

Setting Context size LUT LUT size Code size Total size
0 0.17 KB Flash 0.3 KB 0.5 KB 0.8 KB
1 0.17 KB - - 7.7 KB 7.7 KB
SHA-512

Default

#define CRYPTO_CONFIG_SHA512_OPTIMIZE     0

Override

To define a non-default value, define this symbol in CRYPTO_Conf.h.

Description

Set this preprocessor symbol to zero to optimize the SHA-512 hash functions for size rather than for speed. When optimized for speed, the SHA-512 function is open coded and faster, but is significantly larger.

Profile

The following table shows required context size, lookup table (LUT) size, and code size in kilobytes for each configuration value. All values are approximate and for a Cortex-M3 processor.

Setting Context size LUT LUT size Code size Total size
0 0.20 KB Flash 0.7 KB  1.1 KB  1.8 KB
1 0.20 KB Flash 0.7 KB 10.3 KB 11.0 KB
2 0.20 KB Flash 0.1 KB 41.5 KB 41.6 KB

Performance and resource use

This chapter describes the memory requirements and performance of emSecure-RSA.

Performance

RSA sign and verify performance can be benchmarked with the application SECURE_RSA_Bench_Performance.c.

The following is the output for the Cortex-M4 on a SEGGER emPower board:

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA Performance Benchmark compiled May 19 2017 09:24:12

Compiler: clang 4.0.0 (tags/RELEASE_400/final)
System:   Processor speed           = 168.000 MHz
Config:   CRYPTO_VERSION            = 20000 [2.00]
Config:   SECURE_RSA_VERSION        = 23000 [2.30]
Config:   CRYPTO_MPI_BITS_PER_LIMB  = 32
Config:   SECURE_RSA_MAX_KEY_LENGTH = 2048 bits

Sign/Verify Performance
=======================

+----------+----------+----------+----------+
|  Modulus |  Message |     Sign |   Verify |
|    /bits |   /bytes |      /ms |      /ms |
+----------+----------+----------+----------+
|      512 |        0 |    32.03 |     1.95 |
|      512 |     1024 |    32.19 |     2.22 |
|      512 |   102400 |    55.16 |    25.22 |
+----------+----------+----------+----------+
|     1024 |        0 |   146.71 |     5.05 |
|     1024 |     1024 |   147.00 |     5.32 |
|     1024 |   102400 |   169.83 |    28.33 |
+----------+----------+----------+----------+
|     2048 |        0 |   847.00 |    17.61 |
|     2048 |     1024 |   846.00 |    17.82 |
|     2048 |   102400 |   869.00 |    40.76 |
+----------+----------+----------+----------+

Benchmark complete

SECURE_RSA_Bench_Performance.c listing

/*********************************************************************
*                     SEGGER Microcontroller GmbH                    *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*       (c) 2003 - 2019  SEGGER Microcontroller GmbH                 *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File        : SECURE_RSA_Bench_Performance.c
Purpose     : Benchmark emSecure-RSA performance.

Additional information:
  Preparations:
    None.

  Expected behavior:
    Measures the sign and verify performance using RSA signatures.
    For more detailed information see the emSecure RSA manual.

  Sample output:
    (c) 2014-2018 SEGGER Microcontroller GmbH    www.segger.com
    emSecure-RSA Performance Benchmark compiled Jan 28 2019 14:11:32

    Compiler: gcc 7.3.1
    System:   Processor speed             = 204.000 MHz
    Config:   CRYPTO_VERSION              = 23000 [2.30]
    Config:   SECURE_RSA_VERSION          = 23800 [2.38]
    Config:   CRYPTO_MPI_BITS_PER_LIMB    = 32
    Config:   SECURE_RSA_MAX_KEY_LENGTH   = 2048 bits
    Config:   SECURE_RSA_HASH_FUNCTION    = SHA1
    Config:   SECURE_RSA_SIGNATURE_SCHEME = PSS

    Sign/Verify Performance
    =======================

    +----------+----------+----------+----------+
    |  Modulus |  Message |     Sign |   Verify |
    |    /bits |   /bytes |      /ms |      /ms |
    +----------+----------+----------+----------+
    |      512 |        0 |    26.49 |     1.89 |
    |      512 |     1024 |    26.73 |     2.27 |
    |      512 |   102400 |    59.58 |    35.15 |
    +----------+----------+----------+----------+
    |     1024 |        0 |   136.93 |     5.40 |
    |     1024 |     1024 |   137.16 |     5.77 |
    |     1024 |   102400 |   169.98 |    38.68 |
    +----------+----------+----------+----------+
    |     2048 |        0 |   882.76 |    20.64 |
    |     2048 |     1024 |   881.14 |    20.94 |
    |     2048 |   102400 |   914.51 |    53.77 |
    +----------+----------+----------+----------+

    Benchmark complete

    STOP.

*/

/*********************************************************************
*
*       #include section
*
**********************************************************************
*/

#include "SECURE_RSA.h"
#include "SECURE_RSA_PrivateKey_512b.h"
#include "SECURE_RSA_PrivateKey_1024b.h"
#include "SECURE_RSA_PrivateKey_2048b.h"
#include "SECURE_RSA_PublicKey_512b.h"
#include "SECURE_RSA_PublicKey_1024b.h"
#include "SECURE_RSA_PublicKey_2048b.h"
#include "SEGGER_SYS.h"

/*********************************************************************
*
*       Defines, fixed
*
**********************************************************************
*/

#define STRINGIZE(X)   #X
#define STRINGIZEX(X)  STRINGIZE(X)

/*********************************************************************
*
*       Local data types
*
**********************************************************************
*/

typedef struct {
  const CRYPTO_RSA_PRIVATE_KEY * pPrivateKey;
  const CRYPTO_RSA_PUBLIC_KEY  * pPublicKey;
  const U8                     * pMesssage;
  unsigned                       MessageLen;
} BENCH_PARA;

/*********************************************************************
*
*       Static const data
*
**********************************************************************
*/

static const U8 _aMessage_100k[100*1024] = {
  0x00,
};

static const BENCH_PARA _aBenchKeys[] = {
  { &_SECURE_RSA_PrivateKey_512b,  &_SECURE_RSA_PublicKey_512b,  _aMessage_100k,        0u },
  { &_SECURE_RSA_PrivateKey_512b,  &_SECURE_RSA_PublicKey_512b,  _aMessage_100k,     1024u },
  { &_SECURE_RSA_PrivateKey_512b,  &_SECURE_RSA_PublicKey_512b,  _aMessage_100k, 100*1024u },
  { NULL,                          NULL,                         NULL,                  0u },
  { &_SECURE_RSA_PrivateKey_1024b, &_SECURE_RSA_PublicKey_1024b, _aMessage_100k,        0u },
  { &_SECURE_RSA_PrivateKey_1024b, &_SECURE_RSA_PublicKey_1024b, _aMessage_100k,     1024u },
  { &_SECURE_RSA_PrivateKey_1024b, &_SECURE_RSA_PublicKey_1024b, _aMessage_100k, 100*1024u },
  { NULL,                          NULL,                         NULL,                  0u },
  { &_SECURE_RSA_PrivateKey_2048b, &_SECURE_RSA_PublicKey_2048b, _aMessage_100k,        0u },
  { &_SECURE_RSA_PrivateKey_2048b, &_SECURE_RSA_PublicKey_2048b, _aMessage_100k,     1024u },
  { &_SECURE_RSA_PrivateKey_2048b, &_SECURE_RSA_PublicKey_2048b, _aMessage_100k, 100*1024u }
};

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

static union {
  //
  // Temporary memory used for signing
  //
  CRYPTO_MPI_LIMB       aWs[5][CRYPTO_MPI_LIMBS_REQUIRED(SECURE_RSA_MAX_KEY_LENGTH) + 2];
  //
  // Temporary memory used for verifing
  //
  CRYPTO_MPI_LIMB       aWv[4][(CRYPTO_MPI_LIMBS_REQUIRED(SECURE_RSA_MAX_KEY_LENGTH*2) + 2)];
} _Workspace;

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

/*********************************************************************
*
*       _ConvertTicksToSeconds()
*
*  Function description
*    Convert ticks to seconds.
*
*  Parameters
*    Ticks - Number of ticks reported by SEGGER_SYS_OS_GetTimer().
*
*  Return value
*    Number of seconds corresponding to tick.
*/
static float _ConvertTicksToSeconds(U64 Ticks) {
  return SEGGER_SYS_OS_ConvertTicksToMicros(Ticks) / 1000000.0f;
}

/*********************************************************************
*
*       _BenchmarkSignVerify()
*
*  Function description
*    Count the number of signs and verifies completed in one second.
*
*  Parameters
*    pPara - Pointer to benchmark parameters.
*/
static void _BenchmarkSignVerify(const BENCH_PARA *pPara) {
  U8    aSignature[270];
  U64   OneSecond;
  U64   T0;
  U64   Elapsed;
  int   SignatureLen;
  int   Loops;
  int   Status;
  float Time;
  SEGGER_MEM_CHUNK_HEAP Heap;
  SEGGER_MEM_CONTEXT    Context;
  //
  SEGGER_SYS_IO_Printf("| %8d | %8u | ",
                       CRYPTO_MPI_BitCount(&pPara->pPublicKey->N),
                       pPara->MessageLen);
  //
  Loops = 0;
  OneSecond = SEGGER_SYS_OS_ConvertMicrosToTicks(1000000);
  T0 = SEGGER_SYS_OS_GetTimer();
  do {
    SEGGER_MEM_CHUNK_HEAP_Init(&Context, &Heap, _Workspace.aWs, SEGGER_COUNTOF(_Workspace.aWs), sizeof(_Workspace.aWs[0]));
    SignatureLen = SECURE_RSA_SignEx(pPara->pPrivateKey,
                                     NULL,             0,
                                     pPara->pMesssage, pPara->MessageLen,
                                     aSignature,       sizeof(aSignature),
                                     &Context);
    Elapsed = SEGGER_SYS_OS_GetTimer() - T0;
    ++Loops;
  } while (SignatureLen >= 0 && Elapsed < OneSecond);
  //
  Time = 1000.0f * _ConvertTicksToSeconds(Elapsed) / Loops;
  if (SignatureLen < 0) {
    SEGGER_SYS_IO_Printf("%8s | ", "-Fail-");
  } else {
    SEGGER_SYS_IO_Printf("%8.2f | ", Time);
  }
  //
  if (SignatureLen < 0) {
    SEGGER_SYS_IO_Printf("%8s |\n", "-Skip-");
  } else {
    Loops = 0;
    OneSecond = SEGGER_SYS_OS_ConvertMicrosToTicks(1000000);
    T0 = SEGGER_SYS_OS_GetTimer();
    do {
      SEGGER_MEM_CHUNK_HEAP_Init(&Context, &Heap, _Workspace.aWv, SEGGER_COUNTOF(_Workspace.aWv), sizeof(_Workspace.aWv[0]));
      Status = SECURE_RSA_VerifyEx(pPara->pPublicKey,
                                   NULL,             0,
                                   pPara->pMesssage, pPara->MessageLen,
                                   aSignature,       SignatureLen,
                                   &Context);
      Elapsed = SEGGER_SYS_OS_GetTimer() - T0;
      ++Loops;
    } while (Status >= 0 && Elapsed < OneSecond);
    //
    Time = 1000.0f * _ConvertTicksToSeconds(Elapsed) / Loops;
    if (Status <= 0) {
      SEGGER_SYS_IO_Printf("%8s |\n", "-Fail-");
    } else {
      SEGGER_SYS_IO_Printf("%8.2f |\n", Time);
    }
  }
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/

/*********************************************************************
*
*       MainTask()
*
*  Function description
*    Main entry point for application to run all the tests.
*/
void MainTask(void);
void MainTask(void) {
  unsigned i;
  //
  SECURE_RSA_Init();
  SEGGER_SYS_Init();
  //
  SEGGER_SYS_IO_Printf("\n");
  SEGGER_SYS_IO_Printf("%s    www.segger.com\n", SECURE_RSA_GetCopyrightText());
  SEGGER_SYS_IO_Printf("emSecure-RSA Performance Benchmark compiled " __DATE__ " " __TIME__ "\n\n");
  //
  SEGGER_SYS_IO_Printf("Compiler: %s\n", SEGGER_SYS_GetCompiler());
  if (SEGGER_SYS_GetProcessorSpeed() > 0) {
    SEGGER_SYS_IO_Printf("System:   Processor speed             = %.3f MHz\n",
                         (float)SEGGER_SYS_GetProcessorSpeed() / 1000000.0f);
  }
  SEGGER_SYS_IO_Printf("Config:   CRYPTO_VERSION              = %u [%s]\n", CRYPTO_VERSION, CRYPTO_GetVersionText());
  SEGGER_SYS_IO_Printf("Config:   SECURE_RSA_VERSION          = %u [%s]\n", SECURE_RSA_VERSION, SECURE_RSA_GetVersionText());
  SEGGER_SYS_IO_Printf("Config:   CRYPTO_MPI_BITS_PER_LIMB    = %u\n",      CRYPTO_MPI_BITS_PER_LIMB);
  SEGGER_SYS_IO_Printf("Config:   SECURE_RSA_MAX_KEY_LENGTH   = %u bits\n", SECURE_RSA_MAX_KEY_LENGTH);
  SEGGER_SYS_IO_Printf("Config:   SECURE_RSA_HASH_FUNCTION    = %s\n",      STRINGIZEX(SECURE_RSA_HASH_FUNCTION));
  SEGGER_SYS_IO_Printf("Config:   SECURE_RSA_SIGNATURE_SCHEME = %s\n",      STRINGIZEX(SECURE_RSA_SIGNATURE_SCHEME));
  SEGGER_SYS_IO_Printf("\n");
  //
  SEGGER_SYS_IO_Printf("Sign/Verify Performance\n");
  SEGGER_SYS_IO_Printf("=======================\n\n");
  //
  SEGGER_SYS_IO_Printf("+----------+----------+----------+----------+\n");
  SEGGER_SYS_IO_Printf("|  Modulus |  Message |     Sign |   Verify |\n");
  SEGGER_SYS_IO_Printf("|    /bits |   /bytes |      /ms |      /ms |\n");
  SEGGER_SYS_IO_Printf("+----------+----------+----------+----------+\n");
  for (i = 0; i < SEGGER_COUNTOF(_aBenchKeys); ++i) {
    if (_aBenchKeys[i].pPublicKey == NULL) {
      SEGGER_SYS_IO_Printf("+----------+----------+----------+----------+\n");
    } else {
      _BenchmarkSignVerify(&_aBenchKeys[i]);
    }
  }
  SEGGER_SYS_IO_Printf("+----------+----------+----------+----------+\n");
  SEGGER_SYS_IO_Printf("\n");
  //
  SEGGER_SYS_IO_Printf("Benchmark complete\n");
  SEGGER_SYS_OS_PauseBeforeHalt();
  SEGGER_SYS_OS_Halt(0);
}

/*************************** End of file ****************************/

Memory footprint

The following table lists the memory requirements of emSecure-RSA configured for operation with a 2048 bit key. There are two components to the ROM use, one for the emSecure-RSA code and one for the emCrypt component that can be shared with other security products such as emSecure-ECDSA, emSSL, and emSSH.

Process ROM (SECURE) ROM (CRYPTO) Total ROM Static RAM Stack
Sign only 0.21 KB 5.42 KB 5.63 KB 0.01 KB 2.12 KB
Verify only 0.21 KB 4.35 KB 4.56 KB 0.01 KB 2.93 KB
Sign and verify 0.34 KB 5.70 KB 6.04 KB 0.01 KB 2.93 KB

Test configuration:

Frequently asked questions

Reference

Technical background

emSecure makes use of the RSA-PSS signature scheme, following the “hash-then-sign” paradigm.

A hash of the data to be signed is generated and transformed to create an encoded message which is as long as the private key modulus length. The encoded message is then RSA encrypted.

On verification the message is decrypted again, the encoded message is checked for consistency and the hash is compared with the hash generated from the data to be verified.

emSecure Signing Technical Details

The emSecure signing operation starts by using a secure hash algorithm (SHA1) to generate a hash from the original data. Then using the 2kbit RSA private key along with the hash a digital signature is generated using RSA encryption.

Signing theory

emSecure Verification Technical Details:

The emSecure verification process starts with the data one wishes to verify and the digital signature which was created from the original file. A hash file is generated for the unverified data. The public key and RSA decryption is used to generate the original hash and then compared to verify whether the data file is genuine.

Verification theory

RSA

RSA (named after Ron Rivest, Adi Shamir and Leonard Adleman) is the first published asymmetric public-key cryptosystem algorithm.

RSA uses a set of two keys. One key, the public key, for encryption of data and verification of digital signatures, the other key, the private key, for decrypting and digitally signing data.

RSA keys are generated using two large prime numbers (P and Q). With the knowledge of the public key, consisting of the product of the two prime factors (the modulus, N) and an auxiliary value (E) it is possible to encrypt a message which can then only be decrypted with the private key, but due to the problem of factoring a number with large enough prime factors, there is no known efficient method to get the private key’s value (D) or the two primes from the public key, when the prime factors are kept secret.

For emSecure-RSA, the modulus (N) can be chosen to be of a width between 512 and 4096 bits. In practice RSA is presumed to be safe if N is large enough. In 2010 an RSA modulus of 768 bits has been factorized, there is no known factorization of a higher number.

A modulus of 1024 bits is graded as commercial grade, whereas a 2048 bit modulus is of military grade. Both widths are presumed to be safe for now.

RSA is often used in hybrid cryptosystems because it is known to be 1,000 times slower than symmetric algorithms such as AES. Therefore RSA is used to exchange the encrypted password or initialization vector for symmetric encrypted communication.

Encryption algorithm Decryption algorithm
c = m^E mod(N) m = c^D mod(N)

m: Message c: Ciphertext

E: Public value D: Private value N: Modulus (P*Q)

Signature scheme

PKCS #1

PKCS #1, the Public-Key Cryptography Standards, is the first set of standards for cryptography, published by RSA Laboratories. It describes the definitions and recommendations for RSA as well as the properties of public and private keys, primitive operations and secure cryptographic schemes.

RSA-PSS is part of PKCS #1 V2.1.

http://www.ietf.org/rfc/rfc3447.txt

RSA-PSS

RSA-PSS is a signature scheme based on RSA that provides secure digital signatures. PSS refers to the Probabilistic Signature Scheme by Mihir Bellare and Phillip Rogaway. RSA-PSS follows the “hash-then-sign” paradigm. Instead of encrypting a message, the hash value of the message is encrypted.

A signature is generated in three steps:

It is verified in the same way:

SHA-1

SHA, Secure Hash Algorithm, is a set of cryptographic hash functions. The hash functions were designed by the U.S. National Security Agency (NSA) and NIST as a Federal Information Processing Standard (FIPS).

Secure Hash Algorithms are used to create a unique hash value for any message which is used as the base for digital signatures and provides the integrity of a message. This requires the collision resistance of hash values, the should be practically no two different messages with the same hash value.

SHA-1 produces a 160-bit secure hash of a message of up to 2^64 - 1 bits.

MGF

MGF, a Mask Generation Function, creates an output string of a desired length from an input of variable length. The output is completely determined by the input and should be pseudo-random.

MGF1, used in the RSA-PSS signature scheme is a mask generation function based on a hash function.

Appendix

Example key pair

The example key pair was generated with emKeyGen, default settings (2048 bit key, proven primes), and the pass phrase “SEGGER - The Embedded Experts”.

C:> emKeyGenRSA -pw "SEGGER - The Embedded Experts" -k SECURE_RSA_Expert_Key

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA KeyGen V2.38 compiled May 18 2017 11:51:53

Generating proven prime key pair with public modulus of 2048 bits
Public encryption exponent is set to 65537
Initial seed is 0xADBE961296F573AD2FA65468E1A8837D
Checking keys are consistent: OK
Writing public key file SECURE_RSA_Expert_Key.pub.
Writing private key file SECURE_RSA_Expert_Key.prv.

C:> _

The two files that are written contain the public key and the private key, together making a matched key pair. The private key is required when signing some data and must be kept private and secure. The public key is required when verifying some signed data and can be distributed without concern for privacy.

The keys are converted to compilable form using emPrintKey:

C:> emPrintKeyRSA SECURE_RSA_Expert_Key.prv -p _SECURE_RSA_PrivateKey_Expert \
      >SECURE_RSA_PrivateKey_Expert.h

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA PrintKey V2.38 compiled May 17 2017 10:43:55

Probing file to determine type of key

C:> emPrintKeyRSA SECURE_RSA_Expert_Key.pub -p _SECURE_RSA_PublicKey_Expert \
      >SECURE_RSA_PublicKey_Expert.h

(c) 2014-2018 SEGGER Microcontroller GmbH & Co. KG    www.segger.com
emSecure-RSA PrintKey V2.38 compiled May 17 2017 10:43:55

Probing file to determine type of key

C:> _

The generated source files are shown in the following sections.

SECURE_RSA_PrivateKey_Expert.h listing

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_D_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0xC1, 0x0E, 0x67, 0x6C),
  CRYPTO_MPI_LIMB_DATA4(0xDE, 0xF0, 0x5E, 0x22),
  CRYPTO_MPI_LIMB_DATA4(0x90, 0xBC, 0xCC, 0xA8),
  CRYPTO_MPI_LIMB_DATA4(0x28, 0xA3, 0x0A, 0x05),
  CRYPTO_MPI_LIMB_DATA4(0x5C, 0x2A, 0xB6, 0x7E),
  CRYPTO_MPI_LIMB_DATA4(0xC2, 0xD5, 0xA3, 0xEC),
  CRYPTO_MPI_LIMB_DATA4(0x1B, 0x77, 0x80, 0x55),
  CRYPTO_MPI_LIMB_DATA4(0xAB, 0x78, 0xC6, 0x40),
  CRYPTO_MPI_LIMB_DATA4(0xFA, 0xC3, 0x0B, 0x9A),
  CRYPTO_MPI_LIMB_DATA4(0x66, 0x8B, 0x8A, 0xB2),
  CRYPTO_MPI_LIMB_DATA4(0x0D, 0x88, 0x68, 0x4B),
  CRYPTO_MPI_LIMB_DATA4(0x3A, 0xFE, 0xC0, 0x75),
  CRYPTO_MPI_LIMB_DATA4(0x4B, 0x80, 0x92, 0xF5),
  CRYPTO_MPI_LIMB_DATA4(0x4D, 0x14, 0xA2, 0xC9),
  CRYPTO_MPI_LIMB_DATA4(0x98, 0x50, 0xE0, 0x47),
  CRYPTO_MPI_LIMB_DATA4(0x33, 0x39, 0x79, 0xE3),
  CRYPTO_MPI_LIMB_DATA4(0x7E, 0x67, 0x85, 0x4D),
  CRYPTO_MPI_LIMB_DATA4(0x12, 0xDE, 0xE9, 0xCD),
  CRYPTO_MPI_LIMB_DATA4(0x54, 0xF3, 0x95, 0xB9),
  CRYPTO_MPI_LIMB_DATA4(0xFA, 0xC0, 0x2A, 0x7A),
  CRYPTO_MPI_LIMB_DATA4(0xEC, 0x5B, 0x9F, 0x7E),
  CRYPTO_MPI_LIMB_DATA4(0x35, 0xCA, 0x31, 0xF1),
  CRYPTO_MPI_LIMB_DATA4(0x7F, 0x4A, 0x7E, 0xC3),
  CRYPTO_MPI_LIMB_DATA4(0xD9, 0xFF, 0xC9, 0x1E),
  CRYPTO_MPI_LIMB_DATA4(0xF7, 0x68, 0x5F, 0x06),
  CRYPTO_MPI_LIMB_DATA4(0xCF, 0x2A, 0x1A, 0x25),
  CRYPTO_MPI_LIMB_DATA4(0xF7, 0x22, 0x21, 0x6E),
  CRYPTO_MPI_LIMB_DATA4(0x42, 0xDF, 0xE9, 0xF2),
  CRYPTO_MPI_LIMB_DATA4(0x70, 0x10, 0xC3, 0x33),
  CRYPTO_MPI_LIMB_DATA4(0xB1, 0xAC, 0x2A, 0x0C),
  CRYPTO_MPI_LIMB_DATA4(0x8E, 0x9C, 0x9E, 0x5B),
  CRYPTO_MPI_LIMB_DATA4(0x30, 0xE0, 0x58, 0x17),
  CRYPTO_MPI_LIMB_DATA4(0x6D, 0x51, 0xB9, 0x00),
  CRYPTO_MPI_LIMB_DATA4(0xD9, 0x7E, 0xC9, 0xEE),
  CRYPTO_MPI_LIMB_DATA4(0xAA, 0x4B, 0xCC, 0xD6),
  CRYPTO_MPI_LIMB_DATA4(0x47, 0x6C, 0xDC, 0xBA),
  CRYPTO_MPI_LIMB_DATA4(0x3C, 0x6E, 0x52, 0x88),
  CRYPTO_MPI_LIMB_DATA4(0xF3, 0xC0, 0x76, 0xA8),
  CRYPTO_MPI_LIMB_DATA4(0xE7, 0x23, 0x27, 0x61),
  CRYPTO_MPI_LIMB_DATA4(0xB9, 0x20, 0x33, 0x4B),
  CRYPTO_MPI_LIMB_DATA4(0x08, 0x8D, 0xEF, 0x36),
  CRYPTO_MPI_LIMB_DATA4(0x1C, 0x63, 0x9F, 0x9E),
  CRYPTO_MPI_LIMB_DATA4(0xF5, 0xA2, 0xDF, 0x11),
  CRYPTO_MPI_LIMB_DATA4(0x5D, 0xDC, 0xE1, 0x11),
  CRYPTO_MPI_LIMB_DATA4(0x24, 0xBE, 0x2E, 0xAD),
  CRYPTO_MPI_LIMB_DATA4(0xD3, 0xB3, 0xC1, 0xDA),
  CRYPTO_MPI_LIMB_DATA4(0xF4, 0xC8, 0x22, 0xC0),
  CRYPTO_MPI_LIMB_DATA4(0xE6, 0x17, 0x54, 0xF5),
  CRYPTO_MPI_LIMB_DATA4(0xAD, 0xCF, 0x94, 0xB9),
  CRYPTO_MPI_LIMB_DATA4(0x92, 0xB9, 0xD5, 0x25),
  CRYPTO_MPI_LIMB_DATA4(0x7A, 0xE7, 0xEC, 0x7B),
  CRYPTO_MPI_LIMB_DATA4(0x64, 0xE6, 0xEB, 0x0C),
  CRYPTO_MPI_LIMB_DATA4(0x70, 0xBD, 0x63, 0xA4),
  CRYPTO_MPI_LIMB_DATA4(0x44, 0xA9, 0x49, 0x03),
  CRYPTO_MPI_LIMB_DATA4(0x74, 0x21, 0x0B, 0x79),
  CRYPTO_MPI_LIMB_DATA4(0xED, 0xFA, 0x1A, 0xFC),
  CRYPTO_MPI_LIMB_DATA4(0x38, 0x7F, 0x11, 0x3A),
  CRYPTO_MPI_LIMB_DATA4(0x81, 0xD2, 0x08, 0xFE),
  CRYPTO_MPI_LIMB_DATA4(0x8D, 0xCB, 0x5C, 0x5B),
  CRYPTO_MPI_LIMB_DATA4(0xBF, 0x06, 0x99, 0x0D),
  CRYPTO_MPI_LIMB_DATA4(0xD8, 0xF0, 0xBD, 0xBA),
  CRYPTO_MPI_LIMB_DATA4(0x4D, 0x93, 0x35, 0xF3),
  CRYPTO_MPI_LIMB_DATA4(0xAA, 0x2F, 0xAA, 0x48),
  CRYPTO_MPI_LIMB_DATA4(0xEC, 0x64, 0xDA, 0x10)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_P_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0xB7, 0x0E, 0x63, 0x39),
  CRYPTO_MPI_LIMB_DATA4(0x88, 0x90, 0x46, 0xFE),
  CRYPTO_MPI_LIMB_DATA4(0xA7, 0xB4, 0x02, 0x91),
  CRYPTO_MPI_LIMB_DATA4(0xE0, 0x82, 0xD0, 0x45),
  CRYPTO_MPI_LIMB_DATA4(0x6F, 0x66, 0x49, 0x71),
  CRYPTO_MPI_LIMB_DATA4(0x76, 0xE6, 0xC2, 0xEB),
  CRYPTO_MPI_LIMB_DATA4(0x36, 0x00, 0xAF, 0xF8),
  CRYPTO_MPI_LIMB_DATA4(0x91, 0xC7, 0x02, 0x76),
  CRYPTO_MPI_LIMB_DATA4(0xA4, 0x72, 0xBB, 0x3C),
  CRYPTO_MPI_LIMB_DATA4(0xD8, 0xC8, 0x48, 0x9E),
  CRYPTO_MPI_LIMB_DATA4(0x09, 0xF5, 0xD7, 0x8F),
  CRYPTO_MPI_LIMB_DATA4(0xAE, 0x5E, 0x10, 0xE8),
  CRYPTO_MPI_LIMB_DATA4(0xDB, 0x6D, 0xD4, 0x90),
  CRYPTO_MPI_LIMB_DATA4(0xD5, 0x0D, 0x20, 0xAB),
  CRYPTO_MPI_LIMB_DATA4(0x0A, 0xC3, 0x18, 0xB1),
  CRYPTO_MPI_LIMB_DATA4(0xF6, 0x01, 0x67, 0xA6),
  CRYPTO_MPI_LIMB_DATA4(0xE9, 0x90, 0x4E, 0x7F),
  CRYPTO_MPI_LIMB_DATA4(0xFA, 0x2B, 0xA6, 0x87),
  CRYPTO_MPI_LIMB_DATA4(0x26, 0xEF, 0x83, 0xA0),
  CRYPTO_MPI_LIMB_DATA4(0x6B, 0x73, 0xA8, 0xD4),
  CRYPTO_MPI_LIMB_DATA4(0x8F, 0xF8, 0x9F, 0x78),
  CRYPTO_MPI_LIMB_DATA4(0x20, 0xEE, 0x7A, 0x5C),
  CRYPTO_MPI_LIMB_DATA4(0x69, 0x53, 0xA2, 0xDE),
  CRYPTO_MPI_LIMB_DATA4(0x66, 0xAE, 0x8C, 0x07),
  CRYPTO_MPI_LIMB_DATA4(0x1A, 0x15, 0x97, 0x81),
  CRYPTO_MPI_LIMB_DATA4(0xE2, 0x1D, 0x42, 0x41),
  CRYPTO_MPI_LIMB_DATA4(0xE9, 0xD2, 0x37, 0x0B),
  CRYPTO_MPI_LIMB_DATA4(0x8A, 0xD7, 0xC1, 0x5A),
  CRYPTO_MPI_LIMB_DATA4(0x6C, 0x51, 0xC6, 0xBF),
  CRYPTO_MPI_LIMB_DATA4(0x87, 0xAB, 0x25, 0xEB),
  CRYPTO_MPI_LIMB_DATA4(0x62, 0x57, 0x56, 0xC4),
  CRYPTO_MPI_LIMB_DATA4(0x22, 0xA8, 0x49, 0xD2)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_Q_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0x89, 0x0C, 0x90, 0x27),
  CRYPTO_MPI_LIMB_DATA4(0x48, 0xE4, 0xEA, 0xFD),
  CRYPTO_MPI_LIMB_DATA4(0x1D, 0xFC, 0x5A, 0x33),
  CRYPTO_MPI_LIMB_DATA4(0x08, 0x07, 0x44, 0x5A),
  CRYPTO_MPI_LIMB_DATA4(0xB9, 0xFA, 0x0C, 0x4A),
  CRYPTO_MPI_LIMB_DATA4(0x63, 0x4D, 0x9C, 0x08),
  CRYPTO_MPI_LIMB_DATA4(0xFB, 0x27, 0x44, 0xF2),
  CRYPTO_MPI_LIMB_DATA4(0xA9, 0x72, 0x3D, 0x6F),
  CRYPTO_MPI_LIMB_DATA4(0x37, 0xB0, 0xBF, 0xE6),
  CRYPTO_MPI_LIMB_DATA4(0x18, 0xE9, 0x29, 0xA5),
  CRYPTO_MPI_LIMB_DATA4(0xDF, 0xCA, 0x4E, 0xFB),
  CRYPTO_MPI_LIMB_DATA4(0x9B, 0xC0, 0xFE, 0x84),
  CRYPTO_MPI_LIMB_DATA4(0xB2, 0xDD, 0xDC, 0xDF),
  CRYPTO_MPI_LIMB_DATA4(0x19, 0x6E, 0x50, 0x23),
  CRYPTO_MPI_LIMB_DATA4(0xD3, 0xCD, 0x14, 0x67),
  CRYPTO_MPI_LIMB_DATA4(0x1C, 0xC5, 0x8A, 0x7B),
  CRYPTO_MPI_LIMB_DATA4(0x90, 0xFD, 0x79, 0x60),
  CRYPTO_MPI_LIMB_DATA4(0xB1, 0x75, 0x54, 0x41),
  CRYPTO_MPI_LIMB_DATA4(0x98, 0x44, 0xC5, 0x66),
  CRYPTO_MPI_LIMB_DATA4(0x6C, 0x1F, 0xB7, 0x59),
  CRYPTO_MPI_LIMB_DATA4(0x35, 0x9E, 0xC3, 0xCA),
  CRYPTO_MPI_LIMB_DATA4(0xE4, 0xD4, 0xC5, 0x65),
  CRYPTO_MPI_LIMB_DATA4(0xFB, 0x4C, 0x7E, 0xCF),
  CRYPTO_MPI_LIMB_DATA4(0x15, 0x82, 0x06, 0xC8),
  CRYPTO_MPI_LIMB_DATA4(0xE8, 0xB9, 0x12, 0x65),
  CRYPTO_MPI_LIMB_DATA4(0xB8, 0x38, 0xE7, 0x57),
  CRYPTO_MPI_LIMB_DATA4(0x48, 0x09, 0xF0, 0x9E),
  CRYPTO_MPI_LIMB_DATA4(0x39, 0x6E, 0x81, 0xAD),
  CRYPTO_MPI_LIMB_DATA4(0xF7, 0x80, 0x79, 0xA1),
  CRYPTO_MPI_LIMB_DATA4(0xBC, 0xFD, 0x32, 0x2A),
  CRYPTO_MPI_LIMB_DATA4(0x19, 0xC2, 0x62, 0x81),
  CRYPTO_MPI_LIMB_DATA4(0x41, 0xA3, 0x73, 0xD8)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_DP_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0x4B, 0xCF, 0xDB, 0xDC),
  CRYPTO_MPI_LIMB_DATA4(0x52, 0x33, 0x3D, 0x8B),
  CRYPTO_MPI_LIMB_DATA4(0x66, 0xC6, 0x20, 0x55),
  CRYPTO_MPI_LIMB_DATA4(0x4E, 0x17, 0x39, 0xB4),
  CRYPTO_MPI_LIMB_DATA4(0x56, 0xDD, 0x9B, 0x3B),
  CRYPTO_MPI_LIMB_DATA4(0xE3, 0xEB, 0x1C, 0xC6),
  CRYPTO_MPI_LIMB_DATA4(0xAB, 0x80, 0xDB, 0x79),
  CRYPTO_MPI_LIMB_DATA4(0x57, 0x01, 0xE1, 0x47),
  CRYPTO_MPI_LIMB_DATA4(0x1B, 0xCE, 0x75, 0x35),
  CRYPTO_MPI_LIMB_DATA4(0xB7, 0xB5, 0x7D, 0x9A),
  CRYPTO_MPI_LIMB_DATA4(0xE6, 0xF1, 0x6B, 0xEB),
  CRYPTO_MPI_LIMB_DATA4(0x0D, 0x3A, 0x74, 0x0B),
  CRYPTO_MPI_LIMB_DATA4(0x08, 0xDB, 0xB5, 0x84),
  CRYPTO_MPI_LIMB_DATA4(0x96, 0x7D, 0xF5, 0x84),
  CRYPTO_MPI_LIMB_DATA4(0x1E, 0x43, 0x8A, 0x84),
  CRYPTO_MPI_LIMB_DATA4(0x84, 0x46, 0x05, 0xE0),
  CRYPTO_MPI_LIMB_DATA4(0x84, 0x78, 0x63, 0xAD),
  CRYPTO_MPI_LIMB_DATA4(0x72, 0xB6, 0x5D, 0xFB),
  CRYPTO_MPI_LIMB_DATA4(0x8A, 0x29, 0x4A, 0x99),
  CRYPTO_MPI_LIMB_DATA4(0xEC, 0x50, 0x7C, 0x54),
  CRYPTO_MPI_LIMB_DATA4(0x1E, 0xD8, 0xE7, 0xC7),
  CRYPTO_MPI_LIMB_DATA4(0x2D, 0x61, 0xAD, 0xBF),
  CRYPTO_MPI_LIMB_DATA4(0x4E, 0x0D, 0x7E, 0x80),
  CRYPTO_MPI_LIMB_DATA4(0x21, 0x7B, 0xAD, 0x85),
  CRYPTO_MPI_LIMB_DATA4(0xAE, 0x1E, 0x95, 0xAF),
  CRYPTO_MPI_LIMB_DATA4(0x9A, 0x0C, 0xAC, 0x09),
  CRYPTO_MPI_LIMB_DATA4(0xE9, 0xA8, 0x4F, 0x77),
  CRYPTO_MPI_LIMB_DATA4(0x3C, 0x1E, 0x05, 0x19),
  CRYPTO_MPI_LIMB_DATA4(0x40, 0xAB, 0x71, 0x93),
  CRYPTO_MPI_LIMB_DATA4(0x8D, 0xE9, 0xD1, 0x65),
  CRYPTO_MPI_LIMB_DATA4(0x2F, 0x05, 0xD7, 0x8A),
  CRYPTO_MPI_LIMB_DATA4(0x0F, 0x29, 0xD1, 0xCF)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_DQ_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0x09, 0x75, 0x79, 0x77),
  CRYPTO_MPI_LIMB_DATA4(0x06, 0x77, 0x27, 0x47),
  CRYPTO_MPI_LIMB_DATA4(0xDA, 0x0C, 0x7D, 0x14),
  CRYPTO_MPI_LIMB_DATA4(0x01, 0x34, 0x37, 0xA1),
  CRYPTO_MPI_LIMB_DATA4(0x20, 0xAF, 0xB4, 0x9A),
  CRYPTO_MPI_LIMB_DATA4(0x77, 0x2B, 0xAC, 0x60),
  CRYPTO_MPI_LIMB_DATA4(0x34, 0x8B, 0x6D, 0x81),
  CRYPTO_MPI_LIMB_DATA4(0xC4, 0x16, 0x9A, 0xDD),
  CRYPTO_MPI_LIMB_DATA4(0x1A, 0xAC, 0x94, 0x5A),
  CRYPTO_MPI_LIMB_DATA4(0x01, 0xDC, 0xF2, 0x0D),
  CRYPTO_MPI_LIMB_DATA4(0x53, 0x94, 0x45, 0x35),
  CRYPTO_MPI_LIMB_DATA4(0x9D, 0x40, 0xE2, 0x63),
  CRYPTO_MPI_LIMB_DATA4(0xBB, 0x7B, 0x3D, 0xE5),
  CRYPTO_MPI_LIMB_DATA4(0xD8, 0x13, 0x6F, 0xED),
  CRYPTO_MPI_LIMB_DATA4(0x3E, 0xFB, 0x4F, 0xB1),
  CRYPTO_MPI_LIMB_DATA4(0xD2, 0x77, 0x43, 0x00),
  CRYPTO_MPI_LIMB_DATA4(0x15, 0x8C, 0xB2, 0x21),
  CRYPTO_MPI_LIMB_DATA4(0x09, 0xBF, 0xBF, 0x17),
  CRYPTO_MPI_LIMB_DATA4(0xA1, 0x94, 0xD9, 0x43),
  CRYPTO_MPI_LIMB_DATA4(0x4D, 0x24, 0xCF, 0x5A),
  CRYPTO_MPI_LIMB_DATA4(0x3E, 0x00, 0x8A, 0xE9),
  CRYPTO_MPI_LIMB_DATA4(0xE9, 0x4A, 0x23, 0x06),
  CRYPTO_MPI_LIMB_DATA4(0x8F, 0xB3, 0x33, 0x75),
  CRYPTO_MPI_LIMB_DATA4(0x03, 0xF8, 0x67, 0x84),
  CRYPTO_MPI_LIMB_DATA4(0xB3, 0xB2, 0xBC, 0xDC),
  CRYPTO_MPI_LIMB_DATA4(0xD7, 0xA7, 0x92, 0x90),
  CRYPTO_MPI_LIMB_DATA4(0x03, 0x01, 0xC6, 0x55),
  CRYPTO_MPI_LIMB_DATA4(0x8A, 0xE7, 0x1C, 0xFB),
  CRYPTO_MPI_LIMB_DATA4(0xB4, 0xA6, 0x48, 0xE1),
  CRYPTO_MPI_LIMB_DATA4(0x25, 0xC2, 0x14, 0xA5),
  CRYPTO_MPI_LIMB_DATA4(0xCF, 0x3A, 0xBF, 0x7D),
  CRYPTO_MPI_LIMB_DATA4(0xEC, 0xC8, 0x5B, 0xCE)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_QInv_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0x8E, 0xA6, 0xAC, 0x41),
  CRYPTO_MPI_LIMB_DATA4(0x06, 0xDC, 0xEA, 0xBA),
  CRYPTO_MPI_LIMB_DATA4(0x6D, 0xBF, 0xC2, 0x82),
  CRYPTO_MPI_LIMB_DATA4(0x66, 0x2C, 0xBE, 0xBC),
  CRYPTO_MPI_LIMB_DATA4(0x74, 0xA1, 0xE3, 0x3B),
  CRYPTO_MPI_LIMB_DATA4(0x31, 0x10, 0x85, 0xE8),
  CRYPTO_MPI_LIMB_DATA4(0x87, 0x19, 0x4D, 0xA1),
  CRYPTO_MPI_LIMB_DATA4(0x47, 0x95, 0xAB, 0x2A),
  CRYPTO_MPI_LIMB_DATA4(0x8B, 0x2F, 0xBD, 0x0E),
  CRYPTO_MPI_LIMB_DATA4(0xAB, 0xF7, 0xCD, 0x2F),
  CRYPTO_MPI_LIMB_DATA4(0xDB, 0x00, 0x24, 0x1C),
  CRYPTO_MPI_LIMB_DATA4(0x9F, 0x56, 0xA7, 0xFF),
  CRYPTO_MPI_LIMB_DATA4(0xFC, 0xB3, 0x67, 0xD9),
  CRYPTO_MPI_LIMB_DATA4(0x24, 0xA5, 0x3C, 0x22),
  CRYPTO_MPI_LIMB_DATA4(0xC3, 0xC1, 0xE9, 0xEE),
  CRYPTO_MPI_LIMB_DATA4(0xDA, 0x45, 0x7B, 0xB6),
  CRYPTO_MPI_LIMB_DATA4(0xA9, 0x0C, 0x26, 0xEB),
  CRYPTO_MPI_LIMB_DATA4(0x29, 0xD3, 0xEA, 0x41),
  CRYPTO_MPI_LIMB_DATA4(0x7C, 0x5E, 0x90, 0x45),
  CRYPTO_MPI_LIMB_DATA4(0x30, 0xD0, 0xDB, 0x28),
  CRYPTO_MPI_LIMB_DATA4(0x71, 0x69, 0x00, 0xD1),
  CRYPTO_MPI_LIMB_DATA4(0xAB, 0x71, 0x55, 0xCF),
  CRYPTO_MPI_LIMB_DATA4(0x6A, 0xC6, 0xEA, 0x7F),
  CRYPTO_MPI_LIMB_DATA4(0x3B, 0x00, 0xD8, 0xC0),
  CRYPTO_MPI_LIMB_DATA4(0xBE, 0x0D, 0x71, 0x4C),
  CRYPTO_MPI_LIMB_DATA4(0xF4, 0x50, 0x25, 0xFB),
  CRYPTO_MPI_LIMB_DATA4(0xB7, 0x96, 0x66, 0x13),
  CRYPTO_MPI_LIMB_DATA4(0x4F, 0x12, 0x15, 0x34),
  CRYPTO_MPI_LIMB_DATA4(0xD7, 0x18, 0xDA, 0x0F),
  CRYPTO_MPI_LIMB_DATA4(0x4D, 0x2D, 0xDF, 0x47),
  CRYPTO_MPI_LIMB_DATA4(0x8D, 0x6E, 0x1C, 0xCD),
  CRYPTO_MPI_LIMB_DATA4(0x86, 0xE1, 0xA5, 0xAF)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PrivateKey_Expert_N_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0xEF, 0x73, 0xA3, 0x82),
  CRYPTO_MPI_LIMB_DATA4(0x05, 0x3A, 0x25, 0x1B),
  CRYPTO_MPI_LIMB_DATA4(0xC6, 0x77, 0xFE, 0xAE),
  CRYPTO_MPI_LIMB_DATA4(0x77, 0xFA, 0x56, 0x47),
  CRYPTO_MPI_LIMB_DATA4(0xDC, 0x0B, 0x00, 0x91),
  CRYPTO_MPI_LIMB_DATA4(0x08, 0x1D, 0x43, 0xDF),
  CRYPTO_MPI_LIMB_DATA4(0xA1, 0x7A, 0xF6, 0xD9),
  CRYPTO_MPI_LIMB_DATA4(0x0D, 0x15, 0x9E, 0x8A),
  CRYPTO_MPI_LIMB_DATA4(0xD1, 0xE2, 0x20, 0x2E),
  CRYPTO_MPI_LIMB_DATA4(0x4D, 0x1D, 0x54, 0x7C),
  CRYPTO_MPI_LIMB_DATA4(0x66, 0xE7, 0x34, 0xFA),
  CRYPTO_MPI_LIMB_DATA4(0xA1, 0xDF, 0x7F, 0xCE),
  CRYPTO_MPI_LIMB_DATA4(0x82, 0xCF, 0x98, 0x02),
  CRYPTO_MPI_LIMB_DATA4(0xC3, 0x8B, 0xCC, 0xC7),
  CRYPTO_MPI_LIMB_DATA4(0x24, 0xA9, 0x08, 0x7E),
  CRYPTO_MPI_LIMB_DATA4(0x3E, 0x50, 0x2D, 0xE0),
  CRYPTO_MPI_LIMB_DATA4(0xF6, 0xCC, 0x2E, 0x51),
  CRYPTO_MPI_LIMB_DATA4(0x82, 0x38, 0xA7, 0x1D),
  CRYPTO_MPI_LIMB_DATA4(0x2D, 0x17, 0xB1, 0x6C),
  CRYPTO_MPI_LIMB_DATA4(0x45, 0xEA, 0xA6, 0x6C),
  CRYPTO_MPI_LIMB_DATA4(0x11, 0x4E, 0xFF, 0x69),
  CRYPTO_MPI_LIMB_DATA4(0xA0, 0x98, 0x6B, 0x7D),
  CRYPTO_MPI_LIMB_DATA4(0x12, 0x02, 0x60, 0x01),
  CRYPTO_MPI_LIMB_DATA4(0x8A, 0xD0, 0x91, 0x1F),
  CRYPTO_MPI_LIMB_DATA4(0x5E, 0xC7, 0x6D, 0x6E),
  CRYPTO_MPI_LIMB_DATA4(0x6F, 0x6A, 0x8B, 0x8A),
  CRYPTO_MPI_LIMB_DATA4(0xBE, 0xE7, 0x27, 0x4E),
  CRYPTO_MPI_LIMB_DATA4(0xDA, 0x2F, 0x53, 0x1C),
  CRYPTO_MPI_LIMB_DATA4(0xF1, 0xEB, 0x91, 0x90),
  CRYPTO_MPI_LIMB_DATA4(0x62, 0xDA, 0x95, 0x20),
  CRYPTO_MPI_LIMB_DATA4(0xC6, 0xF2, 0x4E, 0x67),
  CRYPTO_MPI_LIMB_DATA4(0x0C, 0x6F, 0x27, 0x05),
  CRYPTO_MPI_LIMB_DATA4(0x3F, 0xC6, 0x67, 0x2F),
  CRYPTO_MPI_LIMB_DATA4(0x75, 0x49, 0xFB, 0xB7),
  CRYPTO_MPI_LIMB_DATA4(0x31, 0x91, 0x5D, 0xC1),
  CRYPTO_MPI_LIMB_DATA4(0x30, 0x59, 0xEC, 0xB0),
  CRYPTO_MPI_LIMB_DATA4(0xC5, 0x6B, 0xB6, 0x7B),
  CRYPTO_MPI_LIMB_DATA4(0x33, 0xBA, 0xE1, 0x31),
  CRYPTO_MPI_LIMB_DATA4(0x59, 0x36, 0x5A, 0x1E),
  CRYPTO_MPI_LIMB_DATA4(0xC0, 0x63, 0xBE, 0xD8),
  CRYPTO_MPI_LIMB_DATA4(0x2C, 0x86, 0xD0, 0x00),
  CRYPTO_MPI_LIMB_DATA4(0x9E, 0xA8, 0x70, 0x45),
  CRYPTO_MPI_LIMB_DATA4(0xC0, 0xA2, 0xBA, 0xB1),
  CRYPTO_MPI_LIMB_DATA4(0xC9, 0xB4, 0xD7, 0x6F),
  CRYPTO_MPI_LIMB_DATA4(0xAB, 0x64, 0x52, 0x37),
  CRYPTO_MPI_LIMB_DATA4(0xE1, 0xE2, 0xF5, 0xD0),
  CRYPTO_MPI_LIMB_DATA4(0x5A, 0xA2, 0x9D, 0x46),
  CRYPTO_MPI_LIMB_DATA4(0xF6, 0xE0, 0x5A, 0x9C),
  CRYPTO_MPI_LIMB_DATA4(0xCC, 0x58, 0xD6, 0xEE),
  CRYPTO_MPI_LIMB_DATA4(0x11, 0xB0, 0xCE, 0xF2),
  CRYPTO_MPI_LIMB_DATA4(0x63, 0x15, 0xD8, 0x9A),
  CRYPTO_MPI_LIMB_DATA4(0x59, 0x69, 0x05, 0x1C),
  CRYPTO_MPI_LIMB_DATA4(0x1D, 0x22, 0x46, 0x7F),
  CRYPTO_MPI_LIMB_DATA4(0x06, 0xAC, 0x10, 0xA6),
  CRYPTO_MPI_LIMB_DATA4(0x40, 0x31, 0x25, 0xF5),
  CRYPTO_MPI_LIMB_DATA4(0x3F, 0x09, 0xEA, 0x52),
  CRYPTO_MPI_LIMB_DATA4(0xFE, 0x18, 0x0B, 0xBB),
  CRYPTO_MPI_LIMB_DATA4(0x56, 0x25, 0x40, 0x3F),
  CRYPTO_MPI_LIMB_DATA4(0x86, 0x44, 0x6B, 0x8F),
  CRYPTO_MPI_LIMB_DATA4(0x11, 0xCE, 0xF4, 0xF8),
  CRYPTO_MPI_LIMB_DATA4(0x56, 0x70, 0x7C, 0x78),
  CRYPTO_MPI_LIMB_DATA4(0x28, 0xFE, 0xEB, 0xEA),
  CRYPTO_MPI_LIMB_DATA4(0xB7, 0x1D, 0x51, 0x92),
  CRYPTO_MPI_LIMB_DATA4(0x0E, 0x23, 0xCD, 0xB1)
};

static const CRYPTO_RSA_PRIVATE_KEY _SECURE_RSA_PrivateKey_Expert = {
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_D_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_P_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_Q_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_DP_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_DQ_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_QInv_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PrivateKey_Expert_N_aLimbs) },
  { CRYPTO_MPI_INIT_RO_ZERO }
};

SECURE_RSA_PublicKey_Expert.h listing

static const CRYPTO_MPI_LIMB _SECURE_RSA_PublicKey_Expert_N_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA4(0xEF, 0x73, 0xA3, 0x82),
  CRYPTO_MPI_LIMB_DATA4(0x05, 0x3A, 0x25, 0x1B),
  CRYPTO_MPI_LIMB_DATA4(0xC6, 0x77, 0xFE, 0xAE),
  CRYPTO_MPI_LIMB_DATA4(0x77, 0xFA, 0x56, 0x47),
  CRYPTO_MPI_LIMB_DATA4(0xDC, 0x0B, 0x00, 0x91),
  CRYPTO_MPI_LIMB_DATA4(0x08, 0x1D, 0x43, 0xDF),
  CRYPTO_MPI_LIMB_DATA4(0xA1, 0x7A, 0xF6, 0xD9),
  CRYPTO_MPI_LIMB_DATA4(0x0D, 0x15, 0x9E, 0x8A),
  CRYPTO_MPI_LIMB_DATA4(0xD1, 0xE2, 0x20, 0x2E),
  CRYPTO_MPI_LIMB_DATA4(0x4D, 0x1D, 0x54, 0x7C),
  CRYPTO_MPI_LIMB_DATA4(0x66, 0xE7, 0x34, 0xFA),
  CRYPTO_MPI_LIMB_DATA4(0xA1, 0xDF, 0x7F, 0xCE),
  CRYPTO_MPI_LIMB_DATA4(0x82, 0xCF, 0x98, 0x02),
  CRYPTO_MPI_LIMB_DATA4(0xC3, 0x8B, 0xCC, 0xC7),
  CRYPTO_MPI_LIMB_DATA4(0x24, 0xA9, 0x08, 0x7E),
  CRYPTO_MPI_LIMB_DATA4(0x3E, 0x50, 0x2D, 0xE0),
  CRYPTO_MPI_LIMB_DATA4(0xF6, 0xCC, 0x2E, 0x51),
  CRYPTO_MPI_LIMB_DATA4(0x82, 0x38, 0xA7, 0x1D),
  CRYPTO_MPI_LIMB_DATA4(0x2D, 0x17, 0xB1, 0x6C),
  CRYPTO_MPI_LIMB_DATA4(0x45, 0xEA, 0xA6, 0x6C),
  CRYPTO_MPI_LIMB_DATA4(0x11, 0x4E, 0xFF, 0x69),
  CRYPTO_MPI_LIMB_DATA4(0xA0, 0x98, 0x6B, 0x7D),
  CRYPTO_MPI_LIMB_DATA4(0x12, 0x02, 0x60, 0x01),
  CRYPTO_MPI_LIMB_DATA4(0x8A, 0xD0, 0x91, 0x1F),
  CRYPTO_MPI_LIMB_DATA4(0x5E, 0xC7, 0x6D, 0x6E),
  CRYPTO_MPI_LIMB_DATA4(0x6F, 0x6A, 0x8B, 0x8A),
  CRYPTO_MPI_LIMB_DATA4(0xBE, 0xE7, 0x27, 0x4E),
  CRYPTO_MPI_LIMB_DATA4(0xDA, 0x2F, 0x53, 0x1C),
  CRYPTO_MPI_LIMB_DATA4(0xF1, 0xEB, 0x91, 0x90),
  CRYPTO_MPI_LIMB_DATA4(0x62, 0xDA, 0x95, 0x20),
  CRYPTO_MPI_LIMB_DATA4(0xC6, 0xF2, 0x4E, 0x67),
  CRYPTO_MPI_LIMB_DATA4(0x0C, 0x6F, 0x27, 0x05),
  CRYPTO_MPI_LIMB_DATA4(0x3F, 0xC6, 0x67, 0x2F),
  CRYPTO_MPI_LIMB_DATA4(0x75, 0x49, 0xFB, 0xB7),
  CRYPTO_MPI_LIMB_DATA4(0x31, 0x91, 0x5D, 0xC1),
  CRYPTO_MPI_LIMB_DATA4(0x30, 0x59, 0xEC, 0xB0),
  CRYPTO_MPI_LIMB_DATA4(0xC5, 0x6B, 0xB6, 0x7B),
  CRYPTO_MPI_LIMB_DATA4(0x33, 0xBA, 0xE1, 0x31),
  CRYPTO_MPI_LIMB_DATA4(0x59, 0x36, 0x5A, 0x1E),
  CRYPTO_MPI_LIMB_DATA4(0xC0, 0x63, 0xBE, 0xD8),
  CRYPTO_MPI_LIMB_DATA4(0x2C, 0x86, 0xD0, 0x00),
  CRYPTO_MPI_LIMB_DATA4(0x9E, 0xA8, 0x70, 0x45),
  CRYPTO_MPI_LIMB_DATA4(0xC0, 0xA2, 0xBA, 0xB1),
  CRYPTO_MPI_LIMB_DATA4(0xC9, 0xB4, 0xD7, 0x6F),
  CRYPTO_MPI_LIMB_DATA4(0xAB, 0x64, 0x52, 0x37),
  CRYPTO_MPI_LIMB_DATA4(0xE1, 0xE2, 0xF5, 0xD0),
  CRYPTO_MPI_LIMB_DATA4(0x5A, 0xA2, 0x9D, 0x46),
  CRYPTO_MPI_LIMB_DATA4(0xF6, 0xE0, 0x5A, 0x9C),
  CRYPTO_MPI_LIMB_DATA4(0xCC, 0x58, 0xD6, 0xEE),
  CRYPTO_MPI_LIMB_DATA4(0x11, 0xB0, 0xCE, 0xF2),
  CRYPTO_MPI_LIMB_DATA4(0x63, 0x15, 0xD8, 0x9A),
  CRYPTO_MPI_LIMB_DATA4(0x59, 0x69, 0x05, 0x1C),
  CRYPTO_MPI_LIMB_DATA4(0x1D, 0x22, 0x46, 0x7F),
  CRYPTO_MPI_LIMB_DATA4(0x06, 0xAC, 0x10, 0xA6),
  CRYPTO_MPI_LIMB_DATA4(0x40, 0x31, 0x25, 0xF5),
  CRYPTO_MPI_LIMB_DATA4(0x3F, 0x09, 0xEA, 0x52),
  CRYPTO_MPI_LIMB_DATA4(0xFE, 0x18, 0x0B, 0xBB),
  CRYPTO_MPI_LIMB_DATA4(0x56, 0x25, 0x40, 0x3F),
  CRYPTO_MPI_LIMB_DATA4(0x86, 0x44, 0x6B, 0x8F),
  CRYPTO_MPI_LIMB_DATA4(0x11, 0xCE, 0xF4, 0xF8),
  CRYPTO_MPI_LIMB_DATA4(0x56, 0x70, 0x7C, 0x78),
  CRYPTO_MPI_LIMB_DATA4(0x28, 0xFE, 0xEB, 0xEA),
  CRYPTO_MPI_LIMB_DATA4(0xB7, 0x1D, 0x51, 0x92),
  CRYPTO_MPI_LIMB_DATA4(0x0E, 0x23, 0xCD, 0xB1)
};

static const CRYPTO_MPI_LIMB _SECURE_RSA_PublicKey_Expert_E_aLimbs[] = {
  CRYPTO_MPI_LIMB_DATA3(0x01, 0x00, 0x01)
};

static const CRYPTO_RSA_PUBLIC_KEY _SECURE_RSA_PublicKey_Expert = {
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PublicKey_Expert_N_aLimbs) },
  { CRYPTO_MPI_INIT_RO(_SECURE_RSA_PublicKey_Expert_E_aLimbs) },
};