Webserver+3d print
Diff: cyclone_crypto/ecdsa.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_crypto/ecdsa.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,584 @@ +/** + * @file ecdsa.c + * @brief ECDSA (Elliptic Curve Digital Signature Algorithm) + * + * @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. + * + * @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 "crypto.h" +#include "ecdsa.h" +#include "mpi.h" +#include "asn1.h" +#include "debug.h" + +//Check crypto library configuration +#if (ECDSA_SUPPORT == ENABLED) + +//ECDSA with SHA-1 OID (1.2.840.10045.4.1) +const uint8_t ECDSA_WITH_SHA1_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01}; +//ECDSA with SHA-224 OID (1.2.840.10045.4.3.1) +const uint8_t ECDSA_WITH_SHA224_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01}; +//ECDSA with SHA-224 OID (1.2.840.10045.4.3.2) +const uint8_t ECDSA_WITH_SHA256_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02}; +//ECDSA with SHA-384 OID (1.2.840.10045.4.3.3) +const uint8_t ECDSA_WITH_SHA384_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03}; +//ECDSA with SHA-512 OID (1.2.840.10045.4.3.4) +const uint8_t ECDSA_WITH_SHA512_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04}; +//ECDSA with SHA-3-224 OID (2.16.840.1.101.3.4.3.9) +const uint8_t ECDSA_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x09}; +//ECDSA with SHA-3-256 OID (2.16.840.1.101.3.4.3.10) +const uint8_t ECDSA_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0A}; +//ECDSA with SHA-3-384 OID (2.16.840.1.101.3.4.3.11) +const uint8_t ECDSA_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0B}; +//ECDSA with SHA-3-512 OID (2.16.840.1.101.3.4.3.12) +const uint8_t ECDSA_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0C}; + + +/** + * @brief Initialize an ECDSA signature + * @param[in] signature Pointer to the ECDSA signature to initialize + **/ + +void ecdsaInitSignature(EcdsaSignature *signature) +{ + //Initialize multiple precision integers + mpiInit(&signature->r); + mpiInit(&signature->s); +} + + +/** + * @brief Release an ECDSA signature + * @param[in] signature Pointer to the ECDSA signature to free + **/ + +void ecdsaFreeSignature(EcdsaSignature *signature) +{ + //Release multiple precision integers + mpiFree(&signature->r); + mpiFree(&signature->s); +} + + +/** + * @brief Encode ECDSA signature using ASN.1 + * @param[in] signature (R, S) integer pair + * @param[out] data Pointer to the buffer where to store the resulting ASN.1 structure + * @param[out] length Length of the ASN.1 structure + * @return Error code + **/ + +error_t ecdsaWriteSignature(const EcdsaSignature *signature, uint8_t *data, size_t *length) +{ + error_t error; + size_t k; + size_t n; + size_t rLen; + size_t sLen; + Asn1Tag tag; + + //Debug message + TRACE_DEBUG("Writing ECDSA signature...\r\n"); + + //Dump (R, S) integer pair + TRACE_DEBUG(" r:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->r); + TRACE_DEBUG(" s:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->s); + + //Calculate the length of R + rLen = mpiGetByteLength(&signature->r); + //Calculate the length of S + sLen = mpiGetByteLength(&signature->s); + + //Make sure the (R, S) integer pair is valid + if(rLen == 0 || sLen == 0) + return ERROR_INVALID_LENGTH; + + //R and S are always encoded in the smallest possible number of octets + if(mpiGetBitValue(&signature->r, (rLen * 8) - 1)) + rLen++; + if(mpiGetBitValue(&signature->s, (sLen * 8) - 1)) + sLen++; + + //The first pass computes the length of the ASN.1 sequence + n = 0; + + //The parameter R is encapsulated within an ASN.1 structure + tag.constructed = FALSE; + tag.objClass = ASN1_CLASS_UNIVERSAL; + tag.objType = ASN1_TYPE_INTEGER; + tag.length = rLen; + tag.value = NULL; + + //Compute the length of the corresponding ASN.1 structure + error = asn1WriteTag(&tag, FALSE, NULL, NULL); + //Any error to report? + if(error) + return error; + + //Update the length of the ASN.1 sequence + n += tag.totalLength; + + //The parameter S is encapsulated within an ASN.1 structure + tag.constructed = FALSE; + tag.objClass = ASN1_CLASS_UNIVERSAL; + tag.objType = ASN1_TYPE_INTEGER; + tag.length = sLen; + tag.value = NULL; + + //Compute the length of the corresponding ASN.1 structure + error = asn1WriteTag(&tag, FALSE, NULL, NULL); + //Any error to report? + if(error) + return error; + + //Update the length of the ASN.1 sequence + n += tag.totalLength; + + //The second pass encodes the ASN.1 structure + k = 0; + + //The (R, S) integer pair is encapsulated within a sequence + tag.constructed = TRUE; + tag.objClass = ASN1_CLASS_UNIVERSAL; + tag.objType = ASN1_TYPE_SEQUENCE; + tag.length = n; + tag.value = NULL; + + //Write the corresponding ASN.1 tag + error = asn1WriteTag(&tag, FALSE, data + k, &n); + //Any error to report? + if(error) + return error; + + //Advance write pointer + k += n; + + //Encode the parameter R using ASN.1 + tag.constructed = FALSE; + tag.objClass = ASN1_CLASS_UNIVERSAL; + tag.objType = ASN1_TYPE_INTEGER; + tag.length = rLen; + tag.value = NULL; + + //Write the corresponding ASN.1 tag + error = asn1WriteTag(&tag, FALSE, data + k, &n); + //Any error to report? + if(error) + return error; + + //Advance write pointer + k += n; + + //Convert R to an octet string + error = mpiWriteRaw(&signature->r, data + k, rLen); + //Any error to report? + if(error) + return error; + + //Advance write pointer + k += rLen; + + //Encode the parameter S using ASN.1 + tag.constructed = FALSE; + tag.objClass = ASN1_CLASS_UNIVERSAL; + tag.objType = ASN1_TYPE_INTEGER; + tag.length = sLen; + tag.value = NULL; + + //Write the corresponding ASN.1 tag + error = asn1WriteTag(&tag, FALSE, data + k, &n); + //Any error to report? + if(error) + return error; + + //Advance write pointer + k += n; + + //Convert S to an octet string + error = mpiWriteRaw(&signature->s, data + k, sLen); + //Any error to report? + if(error) + return error; + + //Advance write pointer + k += sLen; + + //Dump ECDSA signature + TRACE_DEBUG(" signature:\r\n"); + TRACE_DEBUG_ARRAY(" ", data, k); + + //Total length of the ASN.1 structure + *length = k; + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Read an ASN.1 encoded ECDSA signature + * @param[in] data Pointer to the ASN.1 structure to decode + * @param[in] length Length of the ASN.1 structure + * @param[out] signature (R, S) integer pair + * @return Error code + **/ + +error_t ecdsaReadSignature(const uint8_t *data, size_t length, EcdsaSignature *signature) +{ + error_t error; + Asn1Tag tag; + + //Debug message + TRACE_DEBUG("Reading ECDSA signature...\r\n"); + + //Dump ECDSA signature + TRACE_DEBUG(" signature:\r\n"); + TRACE_DEBUG_ARRAY(" ", data, length); + + //Start of exception handling block + do + { + //Display ASN.1 structure + error = asn1DumpObject(data, length, 0); + //Any error to report? + if(error) + break; + + //Read the contents of the ASN.1 structure + error = asn1ReadTag(data, length, &tag); + //Failed to decode ASN.1 tag? + if(error) + break; + + //Enforce encoding, class and type + error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE); + //The tag does not match the criteria? + if(error) + break; + + //Point to the first field + data = tag.value; + length = tag.length; + + //Read the parameter R + error = asn1ReadTag(data, length, &tag); + //Failed to decode ASN.1 tag? + if(error) + break; + + //Enforce encoding, class and type + error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER); + //The tag does not match the criteria? + if(error) + break; + + //Convert the octet string to a multiple precision integer + error = mpiReadRaw(&signature->r, tag.value, tag.length); + //Any error to report? + if(error) + break; + + //Point to the next field + data += tag.totalLength; + length -= tag.totalLength; + + //Read the parameter S + error = asn1ReadTag(data, length, &tag); + //Failed to decode ASN.1 tag? + if(error) + break; + + //Enforce encoding, class and type + error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER); + //The tag does not match the criteria? + if(error) + break; + + //Convert the octet string to a multiple precision integer + error = mpiReadRaw(&signature->s, tag.value, tag.length); + //Any error to report? + if(error) + break; + + //Dump (R, S) integer pair + TRACE_DEBUG(" r:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->r); + TRACE_DEBUG(" s:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->s); + + //End of exception handling block + } while(0); + + //Clean up side effects if necessary + if(error) + ecdsaFreeSignature(signature); + + //Return status code + return error; +} + + +/** + * @brief ECDSA signature generation + * @param[in] params EC domain parameters + * @param[in] prngAlgo PRNG algorithm + * @param[in] prngContext Pointer to the PRNG context + * @param[in] privateKey Signer's ECDSA private key + * @param[in] digest Digest of the message to be signed + * @param[in] digestLength Length in octets of the digest + * @param[out] signature (R, S) integer pair + * @return Error code + **/ + +error_t ecdsaGenerateSignature(const EcDomainParameters *params, + const PrngAlgo *prngAlgo, void *prngContext, const Mpi *privateKey, + const uint8_t *digest, size_t digestLength, EcdsaSignature *signature) +{ + error_t error; + uint_t n; + Mpi k; + Mpi z; + EcPoint r1; + + //Check parameters + if(params == NULL || privateKey == NULL || digest == NULL || signature == NULL) + return ERROR_INVALID_PARAMETER; + + //Debug message + TRACE_DEBUG("ECDSA signature generation...\r\n"); + TRACE_DEBUG(" private key:\r\n"); + TRACE_DEBUG_MPI(" ", privateKey); + TRACE_DEBUG(" digest:\r\n"); + TRACE_DEBUG_ARRAY(" ", digest, digestLength); + + //Initialize multiple precision integers + mpiInit(&k); + mpiInit(&z); + //Initialize EC point + ecInit(&r1); + + //Let N be the bit length of q + n = mpiGetBitLength(¶ms->q); + + //Generated a pseudorandom number + MPI_CHECK(mpiRand(&k, n, prngAlgo, prngContext)); + + //Make sure that 0 < k < q + if(mpiComp(&k, ¶ms->q) >= 0) + mpiShiftRight(&k, 1); + + //Debug message + TRACE_DEBUG(" k:\r\n"); + TRACE_DEBUG_MPI(" ", &k); + + //Compute N = MIN(N, outlen) + n = MIN(n, digestLength * 8); + + //Convert the digest to a multiple precision integer + MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8)); + + //Keep the leftmost N bits of the hash value + if(n % 8) + { + MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8))); + } + + //Debug message + TRACE_DEBUG(" z:\r\n"); + TRACE_DEBUG_MPI(" ", &z); + + //Compute R1 = (x1, y1) = k.G + EC_CHECK(ecMult(params, &r1, &k, ¶ms->g)); + EC_CHECK(ecAffinify(params, &r1, &r1)); + + //Debug message + TRACE_DEBUG(" x1:\r\n"); + TRACE_DEBUG_MPI(" ", &r1.x); + TRACE_DEBUG(" y1:\r\n"); + TRACE_DEBUG_MPI(" ", &r1.y); + + //Compute r = x1 mod q + MPI_CHECK(mpiMod(&signature->r, &r1.x, ¶ms->q)); + + //Compute k ^ -1 mod q + MPI_CHECK(mpiInvMod(&k, &k, ¶ms->q)); + + //Compute s = k ^ -1 * (z + x * r) mod q + MPI_CHECK(mpiMul(&signature->s, privateKey, &signature->r)); + MPI_CHECK(mpiAdd(&signature->s, &signature->s, &z)); + MPI_CHECK(mpiMod(&signature->s, &signature->s, ¶ms->q)); + MPI_CHECK(mpiMulMod(&signature->s, &signature->s, &k, ¶ms->q)); + + //Dump ECDSA signature + TRACE_DEBUG(" r:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->r); + TRACE_DEBUG(" s:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->s); + +end: + //Release multiple precision integers + mpiFree(&k); + mpiFree(&z); + //Release EC point + ecFree(&r1); + + //Clean up side effects if necessary + if(error) + { + //Release (R, S) integer pair + mpiFree(&signature->r); + mpiFree(&signature->s); + } + + //Return status code + return error; +} + + +/** + * @brief ECDSA signature verification + * @param[in] params EC domain parameters + * @param[in] publicKey Signer's ECDSA public key + * @param[in] digest Digest of the message whose signature is to be verified + * @param[in] digestLength Length in octets of the digest + * @param[in] signature (R, S) integer pair + * @return Error code + **/ + +error_t ecdsaVerifySignature(const EcDomainParameters *params, const EcPoint *publicKey, + const uint8_t *digest, size_t digestLength, const EcdsaSignature *signature) +{ + error_t error; + uint_t n; + Mpi w; + Mpi z; + Mpi u1; + Mpi u2; + Mpi v; + EcPoint v0; + EcPoint v1; + + //Check parameters + if(params == NULL || publicKey == NULL || digest == NULL || signature == NULL) + return ERROR_INVALID_PARAMETER; + + //Debug message + TRACE_DEBUG("ECDSA signature verification...\r\n"); + TRACE_DEBUG(" public key X:\r\n"); + TRACE_DEBUG_MPI(" ", &publicKey->x); + TRACE_DEBUG(" public key Y:\r\n"); + TRACE_DEBUG_MPI(" ", &publicKey->y); + TRACE_DEBUG(" digest:\r\n"); + TRACE_DEBUG_ARRAY(" ", digest, digestLength); + TRACE_DEBUG(" r:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->r); + TRACE_DEBUG(" s:\r\n"); + TRACE_DEBUG_MPI(" ", &signature->s); + + //The verifier shall check that 0 < r < q and 0 < s < q. If either + //condition is violated, the signature shall be rejected as invalid + if(mpiCompInt(&signature->r, 0) <= 0 || mpiComp(&signature->r, ¶ms->q) >= 0) + return ERROR_INVALID_SIGNATURE; + if(mpiCompInt(&signature->s, 0) <= 0 || mpiComp(&signature->s, ¶ms->q) >= 0) + return ERROR_INVALID_SIGNATURE; + + //Initialize multiple precision integers + mpiInit(&w); + mpiInit(&z); + mpiInit(&u1); + mpiInit(&u2); + mpiInit(&v); + //Initialize EC points + ecInit(&v0); + ecInit(&v1); + + //Let N be the bit length of q + n = mpiGetBitLength(¶ms->q); + //Compute N = MIN(N, outlen) + n = MIN(n, digestLength * 8); + + //Convert the digest to a multiple precision integer + MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8)); + + //Keep the leftmost N bits of the hash value + if(n % 8) + { + MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8))); + } + + //Compute w = s ^ -1 mod q + MPI_CHECK(mpiInvMod(&w, &signature->s, ¶ms->q)); + //Compute u1 = z * w mod q + MPI_CHECK(mpiMulMod(&u1, &z, &w, ¶ms->q)); + //Compute u2 = r * w mod q + MPI_CHECK(mpiMulMod(&u2, &signature->r, &w, ¶ms->q)); + + //Compute V0 = (x0, y0) = u1.G + u2.Q + EC_CHECK(ecProjectify(params, &v1, publicKey)); + EC_CHECK(ecTwinMult(params, &v0, &u1, ¶ms->g, &u2, &v1)); + EC_CHECK(ecAffinify(params, &v0, &v0)); + + //Debug message + TRACE_DEBUG(" x0:\r\n"); + TRACE_DEBUG_MPI(" ", &v0.x); + TRACE_DEBUG(" y0:\r\n"); + TRACE_DEBUG_MPI(" ", &v0.y); + + //Compute v = x0 mod q + MPI_CHECK(mpiMod(&v, &v0.x, ¶ms->q)); + + //Debug message + TRACE_DEBUG(" v:\r\n"); + TRACE_DEBUG_MPI(" ", &v); + + //If v = r, then the signature is verified. If v does not equal r, + //then the message or the signature may have been modified + if(!mpiComp(&v, &signature->r)) + error = NO_ERROR; + else + error = ERROR_INVALID_SIGNATURE; + +end: + //Release multiple precision integers + mpiFree(&w); + mpiFree(&z); + mpiFree(&u1); + mpiFree(&u2); + mpiFree(&v); + //Release EC points + ecFree(&v0); + ecFree(&v1); + + //Return status code + return error; +} + +#endif +