Webserver+3d print
cyclone_crypto/rsa.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @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