Webserver+3d print

Dependents:   Nucleo

Revision:
0:8918a71cdbe9
diff -r 000000000000 -r 8918a71cdbe9 cyclone_crypto/rsa.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rsa.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,944 @@
+/**
+ * @file rsa.c
+ * @brief RSA public-key cryptography standard
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * @section Description
+ *
+ * RSA is an algorithm for public-key cryptography which is suitable for signing
+ * as well as encryption. Refer to PKCS #1 (RSA Cryptography Standard)
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include "crypto.h"
+#include "rsa.h"
+#include "mpi.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (RSA_SUPPORT == ENABLED)
+
+//PKCS #1 OID (1.2.840.113549.1.1)
+const uint8_t PKCS1_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01};
+//RSA encryption OID (1.2.840.113549.1.1.1)
+const uint8_t RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01};
+//MD5 with RSA encryption OID (1.2.840.113549.1.1.4)
+const uint8_t MD5_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04};
+//SHA-1 with RSA encryption OID (1.2.840.113549.1.1.5)
+const uint8_t SHA1_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05};
+//SHA-256 with RSA encryption OID (1.2.840.113549.1.1.11)
+const uint8_t SHA256_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B};
+//SHA-384 with RSA encryption OID (1.2.840.113549.1.1.12)
+const uint8_t SHA384_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C};
+//SHA-512 with RSA encryption OID (1.2.840.113549.1.1.13)
+const uint8_t SHA512_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D};
+//RSA PKCS #1 v1.5 signature with SHA-3-224 OID (2.16.840.1.101.3.4.3.13)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0D};
+//RSA PKCS #1 v1.5 signature with SHA-3-256 OID (2.16.840.1.101.3.4.3.14)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0E};
+//RSA PKCS #1 v1.5 signature with SHA-3-384 OID (2.16.840.1.101.3.4.3.15)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0F};
+//RSA PKCS #1 v1.5 signature with SHA-3-512 OID (2.16.840.1.101.3.4.3.16)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x10};
+
+
+/**
+ * @brief Initialize a RSA public key
+ * @param[in] key Pointer to the RSA public key to initialize
+ **/
+
+void rsaInitPublicKey(RsaPublicKey *key)
+{
+   //Initialize multiple precision integers
+   mpiInit(&key->n);
+   mpiInit(&key->e);
+}
+
+
+/**
+ * @brief Release a RSA public key
+ * @param[in] key Pointer to the RSA public key to free
+ **/
+
+void rsaFreePublicKey(RsaPublicKey *key)
+{
+   //Free multiple precision integers
+   mpiFree(&key->n);
+   mpiFree(&key->e);
+}
+
+
+/**
+ * @brief Initialize a RSA private key
+ * @param[in] key Pointer to the RSA private key to initialize
+ **/
+
+void rsaInitPrivateKey(RsaPrivateKey *key)
+{
+   //Initialize multiple precision integers
+   mpiInit(&key->n);
+   mpiInit(&key->e);
+   mpiInit(&key->d);
+   mpiInit(&key->p);
+   mpiInit(&key->q);
+   mpiInit(&key->dp);
+   mpiInit(&key->dq);
+   mpiInit(&key->qinv);
+}
+
+
+/**
+ * @brief Release a RSA private key
+ * @param[in] key Pointer to the RSA private key to free
+ **/
+
+void rsaFreePrivateKey(RsaPrivateKey *key)
+{
+   //Free multiple precision integers
+   mpiFree(&key->n);
+   mpiFree(&key->e);
+   mpiFree(&key->d);
+   mpiFree(&key->p);
+   mpiFree(&key->q);
+   mpiFree(&key->dp);
+   mpiFree(&key->dq);
+   mpiFree(&key->qinv);
+}
+
+
+/**
+ * @brief RSA encryption primitive
+ *
+ * The RSA encryption primitive produces a ciphertext representative from
+ * a message representative under the control of a public key
+ *
+ * @param[in] key RSA public key
+ * @param[in] m Message representative
+ * @param[out] c Ciphertext representative
+ * @return Error code
+ **/
+
+error_t rsaep(const RsaPublicKey *key, const Mpi *m, Mpi *c)
+{
+   //Ensure the RSA public key is valid
+   if(!key->n.size || !key->e.size)
+      return ERROR_INVALID_PARAMETER;
+
+   //The message representative m shall be between 0 and n - 1
+   if(mpiCompInt(m, 0) < 0 || mpiComp(m, &key->n) >= 0)
+      return ERROR_OUT_OF_RANGE;
+
+   //Perform modular exponentiation (c = m ^ e mod n)
+   return mpiExpMod(c, m, &key->e, &key->n);
+}
+
+
+/**
+ * @brief RSA decryption primitive
+ *
+ * The RSA decryption primitive recovers the message representative from
+ * the ciphertext representative under the control of a private key
+ *
+ * @param[in] key RSA private key
+ * @param[in] c Ciphertext representative
+ * @param[out] m Message representative
+ * @return Error code
+ **/
+
+error_t rsadp(const RsaPrivateKey *key, const Mpi *c, Mpi *m)
+{
+   error_t error;
+   Mpi m1;
+   Mpi m2;
+   Mpi h;
+
+   //The ciphertext representative c shall be between 0 and n - 1
+   if(mpiCompInt(c, 0) < 0 || mpiComp(c, &key->n) >= 0)
+      return ERROR_OUT_OF_RANGE;
+
+   //Initialize multiple-precision integers
+   mpiInit(&m1);
+   mpiInit(&m2);
+   mpiInit(&h);
+
+   //Use the Chinese remainder algorithm?
+   if(key->n.size && key->p.size && key->q.size &&
+      key->dp.size && key->dq.size && key->qinv.size)
+   {
+      //Compute m1 = c ^ dP mod p
+      MPI_CHECK(mpiExpMod(&m1, c, &key->dp, &key->p));
+      //Compute m2 = c ^ dQ mod q
+      MPI_CHECK(mpiExpMod(&m2, c, &key->dq, &key->q));
+      //Let h = (m1 - m2) * qInv mod p
+      MPI_CHECK(mpiSub(&h, &m1, &m2));
+      MPI_CHECK(mpiMulMod(&h, &h, &key->qinv, &key->p));
+      //Let m = m2 + q * h
+      MPI_CHECK(mpiMul(m, &key->q, &h));
+      MPI_CHECK(mpiAdd(m, m, &m2));
+   }
+   //Use modular exponentiation?
+   else if(key->n.size && key->d.size)
+   {
+      //Let m = c ^ d mod n
+      error = mpiExpMod(m, c, &key->d, &key->n);
+   }
+   //Invalid parameters?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PARAMETER;
+   }
+
+end:
+   //Free previously allocated memory
+   mpiFree(&m1);
+   mpiFree(&m2);
+   mpiFree(&h);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief RSA signature primitive
+ *
+ * The RSA signature primitive produces a signature representative from
+ * a message representative under the control of a private key
+ *
+ * @param[in] key RSA private key
+ * @param[in] m Message representative
+ * @param[out] s Signature representative
+ * @return Error code
+ **/
+
+error_t rsasp1(const RsaPrivateKey *key, const Mpi *m, Mpi *s)
+{
+   //RSASP1 primitive is the same as RSADP except for the names of its
+   //input and output arguments. They are distinguished as they are
+   //intended for different purposes
+   return rsadp(key, m, s);
+}
+
+
+/**
+ * @brief RSA verification primitive
+ *
+ * The RSA verification primitive recovers the message representative from
+ * the signature representative under the control of a public key
+ *
+ * @param[in] key RSA public key
+ * @param[in] s Signature representative
+ * @param[out] m Message representative
+ * @return Error code
+ **/
+
+error_t rsavp1(const RsaPublicKey *key, const Mpi *s, Mpi *m)
+{
+   //RSAVP1 primitive is the same as RSAEP except for the names of its
+   //input and output arguments. They are distinguished as they are
+   //intended for different purposes
+   return rsaep(key, s, m);
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 encryption operation
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @param[in] key Recipient's RSA public key
+ * @param[in] message Message to be encrypted
+ * @param[in] messageLength Length of the message to be encrypted
+ * @param[out] ciphertext Ciphertext resulting from the encryption operation
+ * @param[out] ciphertextLength Length of the resulting ciphertext
+ * @return Error code
+ **/
+
+error_t rsaesPkcs1v15Encrypt(const PrngAlgo *prngAlgo, void *prngContext, const RsaPublicKey *key,
+   const uint8_t *message, size_t messageLength, uint8_t *ciphertext, size_t *ciphertextLength)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t k;
+   uint_t n;
+   uint8_t *p;
+   Mpi m;
+   Mpi c;
+
+   //Check parameters
+   if(key == NULL || message == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(ciphertext == NULL || ciphertextLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 encryption...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Message:\r\n");
+   TRACE_DEBUG_ARRAY("    ", message, messageLength);
+
+   //Initialize multiple-precision integers
+   mpiInit(&m);
+   mpiInit(&c);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the message
+   if((messageLength + 11) > k)
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the buffer where the encoded message EM will be formatted
+   p = ciphertext;
+
+   //The leading 0x00 octet ensures that the encoded message,
+   //converted to an integer, is less than the modulus
+   *(p++) = 0x00;
+   //For a public-key operation, the block type BT shall be 0x02
+   *(p++) = 0x02;
+
+   //Length of the padding string PS
+   n = k - messageLength - 3;
+
+   //Generate the padding string (pseudo-randomly generated non-zero octets)
+   while(n > 0)
+   {
+      //Generate random data
+      error = prngAlgo->read(prngContext, p, n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Parse the resulting octet string
+      for(i = 0, j = 0; j < n; j++)
+      {
+         //Strip any byte with a value of zero
+         if(p[j] != 0)
+            p[i++] = p[j];
+      }
+
+      //Advance data pointer
+      p += i;
+      n -= i;
+   }
+
+   //Append a 0x00 octet to the padding string
+   *(p++) = 0x00;
+   //Copy the message to be encrypted
+   memcpy(p, message, messageLength);
+
+   //Rewind to the beginning of the encoded message
+   p = ciphertext;
+
+   //Debug message
+   TRACE_DEBUG("  Encoded message\r\n");
+   TRACE_DEBUG_ARRAY("    ", p, k);
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the encoded message EM to an integer message representative m
+      error = mpiReadRaw(&m, p, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSAEP encryption primitive
+      error = rsaep(key, &m, &c);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the ciphertext representative c to a ciphertext of length k octets
+      error = mpiWriteRaw(&c, ciphertext, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Length of the resulting ciphertext
+      *ciphertextLength = k;
+
+      //Debug message
+      TRACE_DEBUG("  Ciphertext:\r\n");
+      TRACE_DEBUG_ARRAY("    ", ciphertext, *ciphertextLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mpiFree(&m);
+   mpiFree(&c);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 decryption operation
+ * @param[in] key Recipient's RSA private key
+ * @param[in] ciphertext Ciphertext to be decrypted
+ * @param[in] ciphertextLength Length of the ciphertext to be decrypted
+ * @param[out] message Output buffer where to store the decrypted message
+ * @param[in] messageSize Size of the output buffer
+ * @param[out] messageLength Length of the decrypted message
+ * @return Error code
+ **/
+
+error_t rsaesPkcs1v15Decrypt(const RsaPrivateKey *key, const uint8_t *ciphertext,
+   size_t ciphertextLength, uint8_t *message, size_t messageSize, size_t *messageLength)
+{
+   error_t error;
+   uint_t i;
+   uint_t k;
+   uint8_t *em;
+   Mpi c;
+   Mpi m;
+
+   //Check parameters
+   if(key == NULL || ciphertext == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(message == NULL || messageLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 decryption...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Private exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->d);
+   TRACE_DEBUG("  Prime 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  Prime 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  Prime exponent 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dp);
+   TRACE_DEBUG("  Prime exponent 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dq);
+   TRACE_DEBUG("  Coefficient:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->qinv);
+   TRACE_DEBUG("  Ciphertext:\r\n");
+   TRACE_DEBUG_ARRAY("    ", ciphertext, ciphertextLength);
+
+   //Initialize multiple-precision integers
+   mpiInit(&c);
+   mpiInit(&m);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the ciphertext
+   if(ciphertextLength != k || ciphertextLength < 11)
+      return ERROR_INVALID_LENGTH;
+
+   //Allocate a buffer to store the encoded message EM
+   em = cryptoAllocMem(k);
+   //Failed to allocate memory?
+   if(em == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the ciphertext to an integer ciphertext representative c
+      error = mpiReadRaw(&c, ciphertext, ciphertextLength);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSADP decryption primitive
+      error = rsadp(key, &c, &m);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the message representative m to an encoded message EM of length k octets
+      error = mpiWriteRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("  Encoded message\r\n");
+      TRACE_DEBUG_ARRAY("    ", em, k);
+
+      //The first octet of EM must have a value of 0x00
+      //and the block type BT shall be 0x02
+      if(em[0] != 0x00 || em[1] != 0x02)
+      {
+         //Report an error
+         error = ERROR_UNEXPECTED_VALUE;
+         break;
+      }
+
+      //An octet with hexadecimal value 0x00 is used to separate PS from M
+      for(i = 2; i < k && em[i] != 0x00; i++);
+
+      //Check whether the padding string is valid
+      if(i < 10 || i >= k)
+      {
+         //Report an error
+         error = ERROR_INVALID_PADDING;
+         break;
+      }
+
+      //Ensure that the output buffer is large enough
+      if(messageSize < (k - i - 1))
+      {
+         //Report an error
+         error = ERROR_INVALID_LENGTH;
+         break;
+      }
+
+      //Recover the length of the message
+      *messageLength = k - i - 1;
+      //Copy the message contents
+      memcpy(message, em + i + 1, *messageLength);
+
+      //Debug message
+      TRACE_DEBUG("  Message:\r\n");
+      TRACE_DEBUG_ARRAY("    ", message, *messageLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release multiple precision integers
+   mpiFree(&c);
+   mpiFree(&m);
+   //Free previously allocated memory
+   cryptoFreeMem(em);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 signature generation operation
+ * @param[in] key Signer's RSA private key
+ * @param[in] hash Hash function used to digest the message
+ * @param[in] digest Digest of the message to be signed
+ * @param[out] signature Resulting signature
+ * @param[out] signatureLength Length of the resulting signature
+ * @return Error code
+ **/
+
+error_t rsassaPkcs1v15Sign(const RsaPrivateKey *key, const HashAlgo *hash,
+   const uint8_t *digest, uint8_t *signature, size_t *signatureLength)
+{
+   error_t error;
+   uint_t k;
+   uint8_t *em;
+   Mpi m;
+   Mpi s;
+
+   //Check parameters
+   if(key == NULL || hash == NULL || digest == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(signature == NULL || signatureLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 signature generation...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Private exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->d);
+   TRACE_DEBUG("  Prime 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  Prime 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  Prime exponent 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dp);
+   TRACE_DEBUG("  Prime exponent 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dq);
+   TRACE_DEBUG("  Coefficient:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->qinv);
+   TRACE_DEBUG("  Message digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, hash->digestSize);
+
+   //Initialize multiple-precision integers
+   mpiInit(&m);
+   mpiInit(&s);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+   //Point to the buffer where the encoded message EM will be generated
+   em = signature;
+
+   //Apply the EMSA-PKCS1-v1.5 encoding operation
+   error = emsaPkcs1v15Encode(hash, digest, em, k);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_DEBUG("  Encoded message\r\n");
+   TRACE_DEBUG_ARRAY("    ", em, k);
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the encoded message EM to an integer message representative m
+      error = mpiReadRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSASP1 signature primitive
+      error = rsasp1(key, &m, &s);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the signature representative s to a signature of length k octets
+      error = mpiWriteRaw(&s, signature, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Length of the resulting signature
+      *signatureLength = k;
+
+      //Debug message
+      TRACE_DEBUG("  Signature:\r\n");
+      TRACE_DEBUG_ARRAY("    ", signature, *signatureLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mpiFree(&m);
+   mpiFree(&s);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 signature verification operation
+ * @param[in] key Signer's RSA public key
+ * @param[in] hash Hash function used to digest the message
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] signature Signature to be verified
+ * @param[in] signatureLength Length of the signature to be verified
+ * @return Error code
+ **/
+
+error_t rsassaPkcs1v15Verify(const RsaPublicKey *key, const HashAlgo *hash,
+   const uint8_t *digest, const uint8_t *signature, size_t signatureLength)
+{
+   error_t error;
+   uint_t k;
+   uint8_t *em;
+   const uint8_t *oid;
+   size_t oidLength;
+   const uint8_t *d;
+   size_t dLength;
+   Mpi s;
+   Mpi m;
+
+   //Check parameters
+   if(key == NULL || hash == NULL || digest == NULL || signature == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 signature verification...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Message digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, hash->digestSize);
+   TRACE_DEBUG("  Signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", signature, signatureLength);
+
+   //Initialize multiple-precision integers
+   mpiInit(&s);
+   mpiInit(&m);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the signature
+   if(signatureLength != k)
+      return ERROR_INVALID_LENGTH;
+
+   //Allocate a memory buffer to hold the encoded message
+   em = cryptoAllocMem(k);
+   //Failed to allocate memory?
+   if(em == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the signature to an integer signature representative s
+      error = mpiReadRaw(&s, signature, signatureLength);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSAVP1 verification primitive
+      error = rsavp1(key, &s, &m);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the message representative m to an encoded message EM of length k octets
+      error = mpiWriteRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("  Encoded message\r\n");
+      TRACE_DEBUG_ARRAY("    ", em, k);
+
+      //Parse the encoded message EM
+      error = emsaPkcs1v15Decode(em, k, &oid, &oidLength, &d, &dLength);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Assume an error...
+      error = ERROR_INVALID_SIGNATURE_ALGO;
+      //Ensure the hash algorithm identifier matches the OID
+      if(oidComp(oid, oidLength, hash->oid, hash->oidSize))
+         break;
+      //Check the length of the digest
+      if(dLength != hash->digestSize)
+         break;
+
+      //Compare the message digest
+      error = memcmp(digest, d, dLength) ? ERROR_INVALID_SIGNATURE : NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&m);
+   //Free previously allocated memory
+   cryptoFreeMem(em);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 encoding method
+ * @param[in] hash Hash function used to digest the message
+ * @param[in] digest Digest of the message to be signed
+ * @param[out] em Encoded message
+ * @param[in] emLength Intended length of the encoded message
+ * @return Error code
+ **/
+
+error_t emsaPkcs1v15Encode(const HashAlgo *hash,
+   const uint8_t *digest, uint8_t *em, size_t emLength)
+{
+   uint_t i;
+   size_t paddingLength;
+
+   //Ensure the length of the digest is valid
+   if((hash->oidSize + hash->digestSize + 21) > emLength)
+      return ERROR_INVALID_LENGTH;
+
+   //The leading 0x00 octet ensures that the encoded message,
+   //converted to an integer, is less than the modulus
+   em[0] = 0x00;
+   //Block type 0x01 is used for private-key operations
+   em[1] = 0x01;
+
+   //Compute the length of the padding string PS
+   paddingLength = emLength - hash->oidSize - hash->digestSize - 13;
+   //Fill the padding string with 0xFF
+   memset(em + 2, 0xFF, paddingLength);
+
+   //Point to the byte that follows PS
+   i = paddingLength + 2;
+   //Append a 0x00 octet to PS
+   em[i++] = 0x00;
+
+   //Encode the DigestInfo using ASN.1
+   em[i++] = ASN1_ENCODING_CONSTRUCTED | ASN1_TYPE_SEQUENCE;
+   em[i++] = (uint8_t) (hash->oidSize + hash->digestSize + 8);
+   em[i++] = ASN1_ENCODING_CONSTRUCTED | ASN1_TYPE_SEQUENCE;
+   em[i++] = (uint8_t) (hash->oidSize + 4);
+   em[i++] = ASN1_TYPE_OBJECT_IDENTIFIER;
+   em[i++] = (uint8_t) hash->oidSize;
+
+   //Copy the hash algorithm OID
+   memcpy(em + i, hash->oid, hash->oidSize);
+   i += hash->oidSize;
+
+   //Encode the rest of the ASN.1 structure
+   em[i++] = ASN1_TYPE_NULL;
+   em[i++] = 0;
+   em[i++] = ASN1_TYPE_OCTET_STRING;
+   em[i++] = (uint8_t) hash->digestSize;
+
+   //Append the hash value
+   memcpy(em + i, digest, hash->digestSize);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 decoding method
+ * @param[in] em Encoded message
+ * @param[in] emLength Length of the encoded message
+ * @param[out] oid Hash algorithm OID
+ * @param[out] oidLength Length of the hash algorithm OID
+ * @param[out] digest Digest value
+ * @param[out] digestLength Length of the digest value
+
+ * @return Error code
+ **/
+
+error_t emsaPkcs1v15Decode(const uint8_t *em, size_t emLength, const uint8_t **oid,
+   size_t *oidLength, const uint8_t **digest, size_t *digestLength)
+{
+   error_t error;
+   uint_t i;
+   size_t length;
+   const uint8_t *data;
+   Asn1Tag tag;
+
+   //Check the length of the encoded message EM
+   if(emLength < 11)
+      return ERROR_INVALID_LENGTH;
+
+   //The first octet of EM must have a value of 0x00
+   if(em[0] != 0x00)
+      return ERROR_UNEXPECTED_VALUE;
+   //The block type BT shall be 0x01
+   if(em[1] != 0x01)
+      return ERROR_UNEXPECTED_VALUE;
+
+   //Check the padding string PS
+   for(i = 2; i < emLength; i++)
+   {
+      //A 0x00 octet indicates the end of the padding string
+      if(em[i] == 0x00)
+         break;
+      //Each byte of PS must be set to 0xFF when the block type is 0x01
+      if(em[i] != 0xFF)
+         return ERROR_INVALID_PADDING;
+   }
+
+   //Check whether the padding string is properly terminated
+   if(i >= emLength)
+      return ERROR_INVALID_PADDING;
+   //The length of PS cannot be less than 8 octets
+   if(i < 10)
+      return ERROR_INVALID_PADDING;
+
+   //Point to the DigestInfo structure
+   data = em + i + 1;
+   length = emLength - i - 1;
+
+   //Read the contents of the DigestInfo structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(!tag.constructed || tag.objType != ASN1_TYPE_SEQUENCE)
+      return ERROR_INVALID_TAG;
+
+   //Point to the DigestAlgorithm structure
+   data = tag.value;
+   length = tag.length;
+
+   //Decode the DigestAlgorithm tag
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(!tag.constructed || tag.objType != ASN1_TYPE_SEQUENCE)
+      return ERROR_INVALID_TAG;
+
+   //Save the location of the next tag
+   data += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Decode the AlgorithmIdentifier tag
+   error = asn1ReadTag(tag.value, tag.length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(tag.constructed || tag.objType != ASN1_TYPE_OBJECT_IDENTIFIER)
+      return ERROR_INVALID_TAG;
+
+   //Save the hash algorithm OID
+   *oid = tag.value;
+   *oidLength = tag.length;
+
+   //Decode the DigestValue tag
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(tag.constructed || tag.objType != ASN1_TYPE_OCTET_STRING)
+      return ERROR_INVALID_TAG;
+
+   //Save the hash value
+   *digest = tag.value;
+   *digestLength = tag.length;
+
+   //EM successfully decoded
+   return NO_ERROR;
+}
+
+#endif
+