michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef FREEBL_NO_DEPEND michael@0: #include "stubs.h" michael@0: #endif michael@0: michael@0: michael@0: #include "blapi.h" michael@0: #include "prerr.h" michael@0: #include "secerr.h" michael@0: #include "secmpi.h" michael@0: #include "secitem.h" michael@0: #include "mplogic.h" michael@0: #include "ec.h" michael@0: #include "ecl.h" michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: michael@0: /* michael@0: * Returns true if pointP is the point at infinity, false otherwise michael@0: */ michael@0: PRBool michael@0: ec_point_at_infinity(SECItem *pointP) michael@0: { michael@0: unsigned int i; michael@0: michael@0: for (i = 1; i < pointP->len; i++) { michael@0: if (pointP->data[i] != 0x00) return PR_FALSE; michael@0: } michael@0: michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: /* michael@0: * Computes scalar point multiplication pointQ = k1 * G + k2 * pointP for michael@0: * the curve whose parameters are encoded in params with base point G. michael@0: */ michael@0: SECStatus michael@0: ec_points_mul(const ECParams *params, const mp_int *k1, const mp_int *k2, michael@0: const SECItem *pointP, SECItem *pointQ) michael@0: { michael@0: mp_int Px, Py, Qx, Qy; michael@0: mp_int Gx, Gy, order, irreducible, a, b; michael@0: #if 0 /* currently don't support non-named curves */ michael@0: unsigned int irr_arr[5]; michael@0: #endif michael@0: ECGroup *group = NULL; michael@0: SECStatus rv = SECFailure; michael@0: mp_err err = MP_OKAY; michael@0: int len; michael@0: michael@0: #if EC_DEBUG michael@0: int i; michael@0: char mpstr[256]; michael@0: michael@0: printf("ec_points_mul: params [len=%d]:", params->DEREncoding.len); michael@0: for (i = 0; i < params->DEREncoding.len; i++) michael@0: printf("%02x:", params->DEREncoding.data[i]); michael@0: printf("\n"); michael@0: michael@0: if (k1 != NULL) { michael@0: mp_tohex(k1, mpstr); michael@0: printf("ec_points_mul: scalar k1: %s\n", mpstr); michael@0: mp_todecimal(k1, mpstr); michael@0: printf("ec_points_mul: scalar k1: %s (dec)\n", mpstr); michael@0: } michael@0: michael@0: if (k2 != NULL) { michael@0: mp_tohex(k2, mpstr); michael@0: printf("ec_points_mul: scalar k2: %s\n", mpstr); michael@0: mp_todecimal(k2, mpstr); michael@0: printf("ec_points_mul: scalar k2: %s (dec)\n", mpstr); michael@0: } michael@0: michael@0: if (pointP != NULL) { michael@0: printf("ec_points_mul: pointP [len=%d]:", pointP->len); michael@0: for (i = 0; i < pointP->len; i++) michael@0: printf("%02x:", pointP->data[i]); michael@0: printf("\n"); michael@0: } michael@0: #endif michael@0: michael@0: /* NOTE: We only support uncompressed points for now */ michael@0: len = (params->fieldID.size + 7) >> 3; michael@0: if (pointP != NULL) { michael@0: if ((pointP->data[0] != EC_POINT_FORM_UNCOMPRESSED) || michael@0: (pointP->len != (2 * len + 1))) { michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); michael@0: return SECFailure; michael@0: }; michael@0: } michael@0: michael@0: MP_DIGITS(&Px) = 0; michael@0: MP_DIGITS(&Py) = 0; michael@0: MP_DIGITS(&Qx) = 0; michael@0: MP_DIGITS(&Qy) = 0; michael@0: MP_DIGITS(&Gx) = 0; michael@0: MP_DIGITS(&Gy) = 0; michael@0: MP_DIGITS(&order) = 0; michael@0: MP_DIGITS(&irreducible) = 0; michael@0: MP_DIGITS(&a) = 0; michael@0: MP_DIGITS(&b) = 0; michael@0: CHECK_MPI_OK( mp_init(&Px) ); michael@0: CHECK_MPI_OK( mp_init(&Py) ); michael@0: CHECK_MPI_OK( mp_init(&Qx) ); michael@0: CHECK_MPI_OK( mp_init(&Qy) ); michael@0: CHECK_MPI_OK( mp_init(&Gx) ); michael@0: CHECK_MPI_OK( mp_init(&Gy) ); michael@0: CHECK_MPI_OK( mp_init(&order) ); michael@0: CHECK_MPI_OK( mp_init(&irreducible) ); michael@0: CHECK_MPI_OK( mp_init(&a) ); michael@0: CHECK_MPI_OK( mp_init(&b) ); michael@0: michael@0: if ((k2 != NULL) && (pointP != NULL)) { michael@0: /* Initialize Px and Py */ michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&Px, pointP->data + 1, (mp_size) len) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&Py, pointP->data + 1 + len, (mp_size) len) ); michael@0: } michael@0: michael@0: /* construct from named params, if possible */ michael@0: if (params->name != ECCurve_noName) { michael@0: group = ECGroup_fromName(params->name); michael@0: } michael@0: michael@0: #if 0 /* currently don't support non-named curves */ michael@0: if (group == NULL) { michael@0: /* Set up mp_ints containing the curve coefficients */ michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&Gx, params->base.data + 1, michael@0: (mp_size) len) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&Gy, params->base.data + 1 + len, michael@0: (mp_size) len) ); michael@0: SECITEM_TO_MPINT( params->order, &order ); michael@0: SECITEM_TO_MPINT( params->curve.a, &a ); michael@0: SECITEM_TO_MPINT( params->curve.b, &b ); michael@0: if (params->fieldID.type == ec_field_GFp) { michael@0: SECITEM_TO_MPINT( params->fieldID.u.prime, &irreducible ); michael@0: group = ECGroup_consGFp(&irreducible, &a, &b, &Gx, &Gy, &order, params->cofactor); michael@0: } else { michael@0: SECITEM_TO_MPINT( params->fieldID.u.poly, &irreducible ); michael@0: irr_arr[0] = params->fieldID.size; michael@0: irr_arr[1] = params->fieldID.k1; michael@0: irr_arr[2] = params->fieldID.k2; michael@0: irr_arr[3] = params->fieldID.k3; michael@0: irr_arr[4] = 0; michael@0: group = ECGroup_consGF2m(&irreducible, irr_arr, &a, &b, &Gx, &Gy, &order, params->cofactor); michael@0: } michael@0: } michael@0: #endif michael@0: if (group == NULL) michael@0: goto cleanup; michael@0: michael@0: if ((k2 != NULL) && (pointP != NULL)) { michael@0: CHECK_MPI_OK( ECPoints_mul(group, k1, k2, &Px, &Py, &Qx, &Qy) ); michael@0: } else { michael@0: CHECK_MPI_OK( ECPoints_mul(group, k1, NULL, NULL, NULL, &Qx, &Qy) ); michael@0: } michael@0: michael@0: /* Construct the SECItem representation of point Q */ michael@0: pointQ->data[0] = EC_POINT_FORM_UNCOMPRESSED; michael@0: CHECK_MPI_OK( mp_to_fixlen_octets(&Qx, pointQ->data + 1, michael@0: (mp_size) len) ); michael@0: CHECK_MPI_OK( mp_to_fixlen_octets(&Qy, pointQ->data + 1 + len, michael@0: (mp_size) len) ); michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: #if EC_DEBUG michael@0: printf("ec_points_mul: pointQ [len=%d]:", pointQ->len); michael@0: for (i = 0; i < pointQ->len; i++) michael@0: printf("%02x:", pointQ->data[i]); michael@0: printf("\n"); michael@0: #endif michael@0: michael@0: cleanup: michael@0: ECGroup_free(group); michael@0: mp_clear(&Px); michael@0: mp_clear(&Py); michael@0: mp_clear(&Qx); michael@0: mp_clear(&Qy); michael@0: mp_clear(&Gx); michael@0: mp_clear(&Gy); michael@0: mp_clear(&order); michael@0: mp_clear(&irreducible); michael@0: mp_clear(&a); michael@0: mp_clear(&b); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: /* Generates a new EC key pair. The private key is a supplied michael@0: * value and the public key is the result of performing a scalar michael@0: * point multiplication of that value with the curve's base point. michael@0: */ michael@0: SECStatus michael@0: ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey, michael@0: const unsigned char *privKeyBytes, int privKeyLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: PLArenaPool *arena; michael@0: ECPrivateKey *key; michael@0: mp_int k; michael@0: mp_err err = MP_OKAY; michael@0: int len; michael@0: michael@0: #if EC_DEBUG michael@0: printf("ec_NewKey called\n"); michael@0: #endif michael@0: MP_DIGITS(&k) = 0; michael@0: michael@0: if (!ecParams || !privKey || !privKeyBytes || (privKeyLen < 0)) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Initialize an arena for the EC key. */ michael@0: if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE))) michael@0: return SECFailure; michael@0: michael@0: key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey)); michael@0: if (!key) { michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Set the version number (SEC 1 section C.4 says it should be 1) */ michael@0: SECITEM_AllocItem(arena, &key->version, 1); michael@0: key->version.data[0] = 1; michael@0: michael@0: /* Copy all of the fields from the ECParams argument to the michael@0: * ECParams structure within the private key. michael@0: */ michael@0: key->ecParams.arena = arena; michael@0: key->ecParams.type = ecParams->type; michael@0: key->ecParams.fieldID.size = ecParams->fieldID.size; michael@0: key->ecParams.fieldID.type = ecParams->fieldID.type; michael@0: if (ecParams->fieldID.type == ec_field_GFp) { michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.prime, michael@0: &ecParams->fieldID.u.prime)); michael@0: } else { michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.poly, michael@0: &ecParams->fieldID.u.poly)); michael@0: } michael@0: key->ecParams.fieldID.k1 = ecParams->fieldID.k1; michael@0: key->ecParams.fieldID.k2 = ecParams->fieldID.k2; michael@0: key->ecParams.fieldID.k3 = ecParams->fieldID.k3; michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.a, michael@0: &ecParams->curve.a)); michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.b, michael@0: &ecParams->curve.b)); michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.seed, michael@0: &ecParams->curve.seed)); michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.base, michael@0: &ecParams->base)); michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.order, michael@0: &ecParams->order)); michael@0: key->ecParams.cofactor = ecParams->cofactor; michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.DEREncoding, michael@0: &ecParams->DEREncoding)); michael@0: key->ecParams.name = ecParams->name; michael@0: CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curveOID, michael@0: &ecParams->curveOID)); michael@0: michael@0: len = (ecParams->fieldID.size + 7) >> 3; michael@0: SECITEM_AllocItem(arena, &key->publicValue, 2*len + 1); michael@0: len = ecParams->order.len; michael@0: SECITEM_AllocItem(arena, &key->privateValue, len); michael@0: michael@0: /* Copy private key */ michael@0: if (privKeyLen >= len) { michael@0: memcpy(key->privateValue.data, privKeyBytes, len); michael@0: } else { michael@0: memset(key->privateValue.data, 0, (len - privKeyLen)); michael@0: memcpy(key->privateValue.data + (len - privKeyLen), privKeyBytes, privKeyLen); michael@0: } michael@0: michael@0: /* Compute corresponding public key */ michael@0: CHECK_MPI_OK( mp_init(&k) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&k, key->privateValue.data, michael@0: (mp_size) len) ); michael@0: michael@0: rv = ec_points_mul(ecParams, &k, NULL, NULL, &(key->publicValue)); michael@0: if (rv != SECSuccess) goto cleanup; michael@0: *privKey = key; michael@0: michael@0: cleanup: michael@0: mp_clear(&k); michael@0: if (rv) michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: michael@0: #if EC_DEBUG michael@0: printf("ec_NewKey returning %s\n", michael@0: (rv == SECSuccess) ? "success" : "failure"); michael@0: #endif michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: /* Generates a new EC key pair. The private key is a supplied michael@0: * random value (in seed) and the public key is the result of michael@0: * performing a scalar point multiplication of that value with michael@0: * the curve's base point. michael@0: */ michael@0: SECStatus michael@0: EC_NewKeyFromSeed(ECParams *ecParams, ECPrivateKey **privKey, michael@0: const unsigned char *seed, int seedlen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: rv = ec_NewKey(ecParams, privKey, seed, seedlen); michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: return rv; michael@0: } michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: /* Generate a random private key using the algorithm A.4.1 of ANSI X9.62, michael@0: * modified a la FIPS 186-2 Change Notice 1 to eliminate the bias in the michael@0: * random number generator. michael@0: * michael@0: * Parameters michael@0: * - order: a buffer that holds the curve's group order michael@0: * - len: the length in octets of the order buffer michael@0: * michael@0: * Return Value michael@0: * Returns a buffer of len octets that holds the private key. The caller michael@0: * is responsible for freeing the buffer with PORT_ZFree. michael@0: */ michael@0: static unsigned char * michael@0: ec_GenerateRandomPrivateKey(const unsigned char *order, int len) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: mp_err err; michael@0: unsigned char *privKeyBytes = NULL; michael@0: mp_int privKeyVal, order_1, one; michael@0: michael@0: MP_DIGITS(&privKeyVal) = 0; michael@0: MP_DIGITS(&order_1) = 0; michael@0: MP_DIGITS(&one) = 0; michael@0: CHECK_MPI_OK( mp_init(&privKeyVal) ); michael@0: CHECK_MPI_OK( mp_init(&order_1) ); michael@0: CHECK_MPI_OK( mp_init(&one) ); michael@0: michael@0: /* Generates 2*len random bytes using the global random bit generator michael@0: * (which implements Algorithm 1 of FIPS 186-2 Change Notice 1) then michael@0: * reduces modulo the group order. michael@0: */ michael@0: if ((privKeyBytes = PORT_Alloc(2*len)) == NULL) goto cleanup; michael@0: CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(privKeyBytes, 2*len) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&privKeyVal, privKeyBytes, 2*len) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&order_1, order, len) ); michael@0: CHECK_MPI_OK( mp_set_int(&one, 1) ); michael@0: CHECK_MPI_OK( mp_sub(&order_1, &one, &order_1) ); michael@0: CHECK_MPI_OK( mp_mod(&privKeyVal, &order_1, &privKeyVal) ); michael@0: CHECK_MPI_OK( mp_add(&privKeyVal, &one, &privKeyVal) ); michael@0: CHECK_MPI_OK( mp_to_fixlen_octets(&privKeyVal, privKeyBytes, len) ); michael@0: memset(privKeyBytes+len, 0, len); michael@0: cleanup: michael@0: mp_clear(&privKeyVal); michael@0: mp_clear(&order_1); michael@0: mp_clear(&one); michael@0: if (err < MP_OKAY) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: if (rv != SECSuccess && privKeyBytes) { michael@0: PORT_Free(privKeyBytes); michael@0: privKeyBytes = NULL; michael@0: } michael@0: return privKeyBytes; michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: /* Generates a new EC key pair. The private key is a random value and michael@0: * the public key is the result of performing a scalar point multiplication michael@0: * of that value with the curve's base point. michael@0: */ michael@0: SECStatus michael@0: EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: int len; michael@0: unsigned char *privKeyBytes = NULL; michael@0: michael@0: if (!ecParams) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: len = ecParams->order.len; michael@0: privKeyBytes = ec_GenerateRandomPrivateKey(ecParams->order.data, len); michael@0: if (privKeyBytes == NULL) goto cleanup; michael@0: /* generate public key */ michael@0: CHECK_SEC_OK( ec_NewKey(ecParams, privKey, privKeyBytes, len) ); michael@0: michael@0: cleanup: michael@0: if (privKeyBytes) { michael@0: PORT_ZFree(privKeyBytes, len); michael@0: } michael@0: #if EC_DEBUG michael@0: printf("EC_NewKey returning %s\n", michael@0: (rv == SECSuccess) ? "success" : "failure"); michael@0: #endif michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* Validates an EC public key as described in Section 5.2.2 of michael@0: * X9.62. The ECDH primitive when used without the cofactor does michael@0: * not address small subgroup attacks, which may occur when the michael@0: * public key is not valid. These attacks can be prevented by michael@0: * validating the public key before using ECDH. michael@0: */ michael@0: SECStatus michael@0: EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue) michael@0: { michael@0: #ifndef NSS_DISABLE_ECC michael@0: mp_int Px, Py; michael@0: ECGroup *group = NULL; michael@0: SECStatus rv = SECFailure; michael@0: mp_err err = MP_OKAY; michael@0: int len; michael@0: michael@0: if (!ecParams || !publicValue) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* NOTE: We only support uncompressed points for now */ michael@0: len = (ecParams->fieldID.size + 7) >> 3; michael@0: if (publicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) { michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); michael@0: return SECFailure; michael@0: } else if (publicValue->len != (2 * len + 1)) { michael@0: PORT_SetError(SEC_ERROR_BAD_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: MP_DIGITS(&Px) = 0; michael@0: MP_DIGITS(&Py) = 0; michael@0: CHECK_MPI_OK( mp_init(&Px) ); michael@0: CHECK_MPI_OK( mp_init(&Py) ); michael@0: michael@0: /* Initialize Px and Py */ michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&Px, publicValue->data + 1, (mp_size) len) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&Py, publicValue->data + 1 + len, (mp_size) len) ); michael@0: michael@0: /* construct from named params */ michael@0: group = ECGroup_fromName(ecParams->name); michael@0: if (group == NULL) { michael@0: /* michael@0: * ECGroup_fromName fails if ecParams->name is not a valid michael@0: * ECCurveName value, or if we run out of memory, or perhaps michael@0: * for other reasons. Unfortunately if ecParams->name is a michael@0: * valid ECCurveName value, we don't know what the right error michael@0: * code should be because ECGroup_fromName doesn't return an michael@0: * error code to the caller. Set err to MP_UNDEF because michael@0: * that's what ECGroup_fromName uses internally. michael@0: */ michael@0: if ((ecParams->name <= ECCurve_noName) || michael@0: (ecParams->name >= ECCurve_pastLastCurve)) { michael@0: err = MP_BADARG; michael@0: } else { michael@0: err = MP_UNDEF; michael@0: } michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* validate public point */ michael@0: if ((err = ECPoint_validate(group, &Px, &Py)) < MP_YES) { michael@0: if (err == MP_NO) { michael@0: PORT_SetError(SEC_ERROR_BAD_KEY); michael@0: rv = SECFailure; michael@0: err = MP_OKAY; /* don't change the error code */ michael@0: } michael@0: goto cleanup; michael@0: } michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: cleanup: michael@0: ECGroup_free(group); michael@0: mp_clear(&Px); michael@0: mp_clear(&Py); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: return rv; michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: return SECFailure; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: } michael@0: michael@0: /* michael@0: ** Performs an ECDH key derivation by computing the scalar point michael@0: ** multiplication of privateValue and publicValue (with or without the michael@0: ** cofactor) and returns the x-coordinate of the resulting elliptic michael@0: ** curve point in derived secret. If successful, derivedSecret->data michael@0: ** is set to the address of the newly allocated buffer containing the michael@0: ** derived secret, and derivedSecret->len is the size of the secret michael@0: ** produced. It is the caller's responsibility to free the allocated michael@0: ** buffer containing the derived secret. michael@0: */ michael@0: SECStatus michael@0: ECDH_Derive(SECItem *publicValue, michael@0: ECParams *ecParams, michael@0: SECItem *privateValue, michael@0: PRBool withCofactor, michael@0: SECItem *derivedSecret) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: unsigned int len = 0; michael@0: SECItem pointQ = {siBuffer, NULL, 0}; michael@0: mp_int k; /* to hold the private value */ michael@0: mp_int cofactor; michael@0: mp_err err = MP_OKAY; michael@0: #if EC_DEBUG michael@0: int i; michael@0: #endif michael@0: michael@0: if (!publicValue || !ecParams || !privateValue || michael@0: !derivedSecret) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: MP_DIGITS(&k) = 0; michael@0: memset(derivedSecret, 0, sizeof *derivedSecret); michael@0: len = (ecParams->fieldID.size + 7) >> 3; michael@0: pointQ.len = 2*len + 1; michael@0: if ((pointQ.data = PORT_Alloc(2*len + 1)) == NULL) goto cleanup; michael@0: michael@0: CHECK_MPI_OK( mp_init(&k) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&k, privateValue->data, michael@0: (mp_size) privateValue->len) ); michael@0: michael@0: if (withCofactor && (ecParams->cofactor != 1)) { michael@0: /* multiply k with the cofactor */ michael@0: MP_DIGITS(&cofactor) = 0; michael@0: CHECK_MPI_OK( mp_init(&cofactor) ); michael@0: mp_set(&cofactor, ecParams->cofactor); michael@0: CHECK_MPI_OK( mp_mul(&k, &cofactor, &k) ); michael@0: } michael@0: michael@0: /* Multiply our private key and peer's public point */ michael@0: if (ec_points_mul(ecParams, NULL, &k, publicValue, &pointQ) != SECSuccess) michael@0: goto cleanup; michael@0: if (ec_point_at_infinity(&pointQ)) { michael@0: PORT_SetError(SEC_ERROR_BAD_KEY); /* XXX better error code? */ michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* Allocate memory for the derived secret and copy michael@0: * the x co-ordinate of pointQ into it. michael@0: */ michael@0: SECITEM_AllocItem(NULL, derivedSecret, len); michael@0: memcpy(derivedSecret->data, pointQ.data + 1, len); michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: #if EC_DEBUG michael@0: printf("derived_secret:\n"); michael@0: for (i = 0; i < derivedSecret->len; i++) michael@0: printf("%02x:", derivedSecret->data[i]); michael@0: printf("\n"); michael@0: #endif michael@0: michael@0: cleanup: michael@0: mp_clear(&k); michael@0: michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: } michael@0: michael@0: if (pointQ.data) { michael@0: PORT_ZFree(pointQ.data, 2*len + 1); michael@0: } michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* Computes the ECDSA signature (a concatenation of two values r and s) michael@0: * on the digest using the given key and the random value kb (used in michael@0: * computing s). michael@0: */ michael@0: SECStatus michael@0: ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, michael@0: const SECItem *digest, const unsigned char *kb, const int kblen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: mp_int x1; michael@0: mp_int d, k; /* private key, random integer */ michael@0: mp_int r, s; /* tuple (r, s) is the signature */ michael@0: mp_int n; michael@0: mp_err err = MP_OKAY; michael@0: ECParams *ecParams = NULL; michael@0: SECItem kGpoint = { siBuffer, NULL, 0}; michael@0: int flen = 0; /* length in bytes of the field size */ michael@0: unsigned olen; /* length in bytes of the base point order */ michael@0: unsigned obits; /* length in bits of the base point order */ michael@0: michael@0: #if EC_DEBUG michael@0: char mpstr[256]; michael@0: #endif michael@0: michael@0: /* Initialize MPI integers. */ michael@0: /* must happen before the first potential call to cleanup */ michael@0: MP_DIGITS(&x1) = 0; michael@0: MP_DIGITS(&d) = 0; michael@0: MP_DIGITS(&k) = 0; michael@0: MP_DIGITS(&r) = 0; michael@0: MP_DIGITS(&s) = 0; michael@0: MP_DIGITS(&n) = 0; michael@0: michael@0: /* Check args */ michael@0: if (!key || !signature || !digest || !kb || (kblen < 0)) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: goto cleanup; michael@0: } michael@0: michael@0: ecParams = &(key->ecParams); michael@0: flen = (ecParams->fieldID.size + 7) >> 3; michael@0: olen = ecParams->order.len; michael@0: if (signature->data == NULL) { michael@0: /* a call to get the signature length only */ michael@0: goto finish; michael@0: } michael@0: if (signature->len < 2*olen) { michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: goto cleanup; michael@0: } michael@0: michael@0: michael@0: CHECK_MPI_OK( mp_init(&x1) ); michael@0: CHECK_MPI_OK( mp_init(&d) ); michael@0: CHECK_MPI_OK( mp_init(&k) ); michael@0: CHECK_MPI_OK( mp_init(&r) ); michael@0: CHECK_MPI_OK( mp_init(&s) ); michael@0: CHECK_MPI_OK( mp_init(&n) ); michael@0: michael@0: SECITEM_TO_MPINT( ecParams->order, &n ); michael@0: SECITEM_TO_MPINT( key->privateValue, &d ); michael@0: michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&k, kb, kblen) ); michael@0: /* Make sure k is in the interval [1, n-1] */ michael@0: if ((mp_cmp_z(&k) <= 0) || (mp_cmp(&k, &n) >= 0)) { michael@0: #if EC_DEBUG michael@0: printf("k is outside [1, n-1]\n"); michael@0: mp_tohex(&k, mpstr); michael@0: printf("k : %s \n", mpstr); michael@0: mp_tohex(&n, mpstr); michael@0: printf("n : %s \n", mpstr); michael@0: #endif michael@0: PORT_SetError(SEC_ERROR_NEED_RANDOM); michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* michael@0: ** We do not want timing information to leak the length of k, michael@0: ** so we compute k*G using an equivalent scalar of fixed michael@0: ** bit-length. michael@0: ** Fix based on patch for ECDSA timing attack in the paper michael@0: ** by Billy Bob Brumley and Nicola Tuveri at michael@0: ** http://eprint.iacr.org/2011/232 michael@0: ** michael@0: ** How do we convert k to a value of a fixed bit-length? michael@0: ** k starts off as an integer satisfying 0 <= k < n. Hence, michael@0: ** n <= k+n < 2n, which means k+n has either the same number michael@0: ** of bits as n or one more bit than n. If k+n has the same michael@0: ** number of bits as n, the second addition ensures that the michael@0: ** final value has exactly one more bit than n. Thus, we michael@0: ** always end up with a value that exactly one more bit than n. michael@0: */ michael@0: CHECK_MPI_OK( mp_add(&k, &n, &k) ); michael@0: if (mpl_significant_bits(&k) <= mpl_significant_bits(&n)) { michael@0: CHECK_MPI_OK( mp_add(&k, &n, &k) ); michael@0: } michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.3.2, Step 2 michael@0: ** michael@0: ** Compute kG michael@0: */ michael@0: kGpoint.len = 2*flen + 1; michael@0: kGpoint.data = PORT_Alloc(2*flen + 1); michael@0: if ((kGpoint.data == NULL) || michael@0: (ec_points_mul(ecParams, &k, NULL, NULL, &kGpoint) michael@0: != SECSuccess)) michael@0: goto cleanup; michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.3.3, Step 1 michael@0: ** michael@0: ** Extract the x co-ordinate of kG into x1 michael@0: */ michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&x1, kGpoint.data + 1, michael@0: (mp_size) flen) ); michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.3.3, Step 2 michael@0: ** michael@0: ** r = x1 mod n NOTE: n is the order of the curve michael@0: */ michael@0: CHECK_MPI_OK( mp_mod(&x1, &n, &r) ); michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.3.3, Step 3 michael@0: ** michael@0: ** verify r != 0 michael@0: */ michael@0: if (mp_cmp_z(&r) == 0) { michael@0: PORT_SetError(SEC_ERROR_NEED_RANDOM); michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.3.3, Step 4 michael@0: ** michael@0: ** s = (k**-1 * (HASH(M) + d*r)) mod n michael@0: */ michael@0: SECITEM_TO_MPINT(*digest, &s); /* s = HASH(M) */ michael@0: michael@0: /* In the definition of EC signing, digests are truncated michael@0: * to the length of n in bits. michael@0: * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ michael@0: CHECK_MPI_OK( (obits = mpl_significant_bits(&n)) ); michael@0: if (digest->len*8 > obits) { michael@0: mpl_rsh(&s,&s,digest->len*8 - obits); michael@0: } michael@0: michael@0: #if EC_DEBUG michael@0: mp_todecimal(&n, mpstr); michael@0: printf("n : %s (dec)\n", mpstr); michael@0: mp_todecimal(&d, mpstr); michael@0: printf("d : %s (dec)\n", mpstr); michael@0: mp_tohex(&x1, mpstr); michael@0: printf("x1: %s\n", mpstr); michael@0: mp_todecimal(&s, mpstr); michael@0: printf("digest: %s (decimal)\n", mpstr); michael@0: mp_todecimal(&r, mpstr); michael@0: printf("r : %s (dec)\n", mpstr); michael@0: mp_tohex(&r, mpstr); michael@0: printf("r : %s\n", mpstr); michael@0: #endif michael@0: michael@0: CHECK_MPI_OK( mp_invmod(&k, &n, &k) ); /* k = k**-1 mod n */ michael@0: CHECK_MPI_OK( mp_mulmod(&d, &r, &n, &d) ); /* d = d * r mod n */ michael@0: CHECK_MPI_OK( mp_addmod(&s, &d, &n, &s) ); /* s = s + d mod n */ michael@0: CHECK_MPI_OK( mp_mulmod(&s, &k, &n, &s) ); /* s = s * k mod n */ michael@0: michael@0: #if EC_DEBUG michael@0: mp_todecimal(&s, mpstr); michael@0: printf("s : %s (dec)\n", mpstr); michael@0: mp_tohex(&s, mpstr); michael@0: printf("s : %s\n", mpstr); michael@0: #endif michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.3.3, Step 5 michael@0: ** michael@0: ** verify s != 0 michael@0: */ michael@0: if (mp_cmp_z(&s) == 0) { michael@0: PORT_SetError(SEC_ERROR_NEED_RANDOM); michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* michael@0: ** michael@0: ** Signature is tuple (r, s) michael@0: */ michael@0: CHECK_MPI_OK( mp_to_fixlen_octets(&r, signature->data, olen) ); michael@0: CHECK_MPI_OK( mp_to_fixlen_octets(&s, signature->data + olen, olen) ); michael@0: finish: michael@0: signature->len = 2*olen; michael@0: michael@0: rv = SECSuccess; michael@0: err = MP_OKAY; michael@0: cleanup: michael@0: mp_clear(&x1); michael@0: mp_clear(&d); michael@0: mp_clear(&k); michael@0: mp_clear(&r); michael@0: mp_clear(&s); michael@0: mp_clear(&n); michael@0: michael@0: if (kGpoint.data) { michael@0: PORT_ZFree(kGpoint.data, 2*flen + 1); michael@0: } michael@0: michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: #if EC_DEBUG michael@0: printf("ECDSA signing with seed %s\n", michael@0: (rv == SECSuccess) ? "succeeded" : "failed"); michael@0: #endif michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Computes the ECDSA signature on the digest using the given key michael@0: ** and a random seed. michael@0: */ michael@0: SECStatus michael@0: ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: int len; michael@0: unsigned char *kBytes= NULL; michael@0: michael@0: if (!key) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Generate random value k */ michael@0: len = key->ecParams.order.len; michael@0: kBytes = ec_GenerateRandomPrivateKey(key->ecParams.order.data, len); michael@0: if (kBytes == NULL) goto cleanup; michael@0: michael@0: /* Generate ECDSA signature with the specified k value */ michael@0: rv = ECDSA_SignDigestWithSeed(key, signature, digest, kBytes, len); michael@0: michael@0: cleanup: michael@0: if (kBytes) { michael@0: PORT_ZFree(kBytes, len); michael@0: } michael@0: michael@0: #if EC_DEBUG michael@0: printf("ECDSA signing %s\n", michael@0: (rv == SECSuccess) ? "succeeded" : "failed"); michael@0: #endif michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Checks the signature on the given digest using the key provided. michael@0: ** michael@0: ** The key argument must represent a valid EC public key (a point on michael@0: ** the relevant curve). If it is not a valid point, then the behavior michael@0: ** of this function is undefined. In cases where a public key might michael@0: ** not be valid, use EC_ValidatePublicKey to check. michael@0: */ michael@0: SECStatus michael@0: ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature, michael@0: const SECItem *digest) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: #ifndef NSS_DISABLE_ECC michael@0: mp_int r_, s_; /* tuple (r', s') is received signature) */ michael@0: mp_int c, u1, u2, v; /* intermediate values used in verification */ michael@0: mp_int x1; michael@0: mp_int n; michael@0: mp_err err = MP_OKAY; michael@0: ECParams *ecParams = NULL; michael@0: SECItem pointC = { siBuffer, NULL, 0 }; michael@0: int slen; /* length in bytes of a half signature (r or s) */ michael@0: int flen; /* length in bytes of the field size */ michael@0: unsigned olen; /* length in bytes of the base point order */ michael@0: unsigned obits; /* length in bits of the base point order */ michael@0: michael@0: #if EC_DEBUG michael@0: char mpstr[256]; michael@0: printf("ECDSA verification called\n"); michael@0: #endif michael@0: michael@0: /* Initialize MPI integers. */ michael@0: /* must happen before the first potential call to cleanup */ michael@0: MP_DIGITS(&r_) = 0; michael@0: MP_DIGITS(&s_) = 0; michael@0: MP_DIGITS(&c) = 0; michael@0: MP_DIGITS(&u1) = 0; michael@0: MP_DIGITS(&u2) = 0; michael@0: MP_DIGITS(&x1) = 0; michael@0: MP_DIGITS(&v) = 0; michael@0: MP_DIGITS(&n) = 0; michael@0: michael@0: /* Check args */ michael@0: if (!key || !signature || !digest) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: goto cleanup; michael@0: } michael@0: michael@0: ecParams = &(key->ecParams); michael@0: flen = (ecParams->fieldID.size + 7) >> 3; michael@0: olen = ecParams->order.len; michael@0: if (signature->len == 0 || signature->len%2 != 0 || michael@0: signature->len > 2*olen) { michael@0: PORT_SetError(SEC_ERROR_INPUT_LEN); michael@0: goto cleanup; michael@0: } michael@0: slen = signature->len/2; michael@0: michael@0: SECITEM_AllocItem(NULL, &pointC, 2*flen + 1); michael@0: if (pointC.data == NULL) michael@0: goto cleanup; michael@0: michael@0: CHECK_MPI_OK( mp_init(&r_) ); michael@0: CHECK_MPI_OK( mp_init(&s_) ); michael@0: CHECK_MPI_OK( mp_init(&c) ); michael@0: CHECK_MPI_OK( mp_init(&u1) ); michael@0: CHECK_MPI_OK( mp_init(&u2) ); michael@0: CHECK_MPI_OK( mp_init(&x1) ); michael@0: CHECK_MPI_OK( mp_init(&v) ); michael@0: CHECK_MPI_OK( mp_init(&n) ); michael@0: michael@0: /* michael@0: ** Convert received signature (r', s') into MPI integers. michael@0: */ michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&r_, signature->data, slen) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&s_, signature->data + slen, slen) ); michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.2, Steps 1 and 2 michael@0: ** michael@0: ** Verify that 0 < r' < n and 0 < s' < n michael@0: */ michael@0: SECITEM_TO_MPINT(ecParams->order, &n); michael@0: if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 || michael@0: mp_cmp(&r_, &n) >= 0 || mp_cmp(&s_, &n) >= 0) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: goto cleanup; /* will return rv == SECFailure */ michael@0: } michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.2, Step 3 michael@0: ** michael@0: ** c = (s')**-1 mod n michael@0: */ michael@0: CHECK_MPI_OK( mp_invmod(&s_, &n, &c) ); /* c = (s')**-1 mod n */ michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.2, Step 4 michael@0: ** michael@0: ** u1 = ((HASH(M')) * c) mod n michael@0: */ michael@0: SECITEM_TO_MPINT(*digest, &u1); /* u1 = HASH(M) */ michael@0: michael@0: /* In the definition of EC signing, digests are truncated michael@0: * to the length of n in bits. michael@0: * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ michael@0: CHECK_MPI_OK( (obits = mpl_significant_bits(&n)) ); michael@0: if (digest->len*8 > obits) { /* u1 = HASH(M') */ michael@0: mpl_rsh(&u1,&u1,digest->len*8 - obits); michael@0: } michael@0: michael@0: #if EC_DEBUG michael@0: mp_todecimal(&r_, mpstr); michael@0: printf("r_: %s (dec)\n", mpstr); michael@0: mp_todecimal(&s_, mpstr); michael@0: printf("s_: %s (dec)\n", mpstr); michael@0: mp_todecimal(&c, mpstr); michael@0: printf("c : %s (dec)\n", mpstr); michael@0: mp_todecimal(&u1, mpstr); michael@0: printf("digest: %s (dec)\n", mpstr); michael@0: #endif michael@0: michael@0: CHECK_MPI_OK( mp_mulmod(&u1, &c, &n, &u1) ); /* u1 = u1 * c mod n */ michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.2, Step 4 michael@0: ** michael@0: ** u2 = ((r') * c) mod n michael@0: */ michael@0: CHECK_MPI_OK( mp_mulmod(&r_, &c, &n, &u2) ); michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.3, Step 1 michael@0: ** michael@0: ** Compute u1*G + u2*Q michael@0: ** Here, A = u1.G B = u2.Q and C = A + B michael@0: ** If the result, C, is the point at infinity, reject the signature michael@0: */ michael@0: if (ec_points_mul(ecParams, &u1, &u2, &key->publicValue, &pointC) michael@0: != SECSuccess) { michael@0: rv = SECFailure; michael@0: goto cleanup; michael@0: } michael@0: if (ec_point_at_infinity(&pointC)) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: rv = SECFailure; michael@0: goto cleanup; michael@0: } michael@0: michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&x1, pointC.data + 1, flen) ); michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.4, Step 2 michael@0: ** michael@0: ** v = x1 mod n michael@0: */ michael@0: CHECK_MPI_OK( mp_mod(&x1, &n, &v) ); michael@0: michael@0: #if EC_DEBUG michael@0: mp_todecimal(&r_, mpstr); michael@0: printf("r_: %s (dec)\n", mpstr); michael@0: mp_todecimal(&v, mpstr); michael@0: printf("v : %s (dec)\n", mpstr); michael@0: #endif michael@0: michael@0: /* michael@0: ** ANSI X9.62, Section 5.4.4, Step 3 michael@0: ** michael@0: ** Verification: v == r' michael@0: */ michael@0: if (mp_cmp(&v, &r_)) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: rv = SECFailure; /* Signature failed to verify. */ michael@0: } else { michael@0: rv = SECSuccess; /* Signature verified. */ michael@0: } michael@0: michael@0: #if EC_DEBUG michael@0: mp_todecimal(&u1, mpstr); michael@0: printf("u1: %s (dec)\n", mpstr); michael@0: mp_todecimal(&u2, mpstr); michael@0: printf("u2: %s (dec)\n", mpstr); michael@0: mp_tohex(&x1, mpstr); michael@0: printf("x1: %s\n", mpstr); michael@0: mp_todecimal(&v, mpstr); michael@0: printf("v : %s (dec)\n", mpstr); michael@0: #endif michael@0: michael@0: cleanup: michael@0: mp_clear(&r_); michael@0: mp_clear(&s_); michael@0: mp_clear(&c); michael@0: mp_clear(&u1); michael@0: mp_clear(&u2); michael@0: mp_clear(&x1); michael@0: mp_clear(&v); michael@0: mp_clear(&n); michael@0: michael@0: if (pointC.data) SECITEM_FreeItem(&pointC, PR_FALSE); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: #if EC_DEBUG michael@0: printf("ECDSA verification %s\n", michael@0: (rv == SECSuccess) ? "succeeded" : "failed"); michael@0: #endif michael@0: #else michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: return rv; michael@0: } michael@0: