1*f9fbec18Smcpowers /* 2*f9fbec18Smcpowers * ***** BEGIN LICENSE BLOCK ***** 3*f9fbec18Smcpowers * Version: MPL 1.1/GPL 2.0/LGPL 2.1 4*f9fbec18Smcpowers * 5*f9fbec18Smcpowers * The contents of this file are subject to the Mozilla Public License Version 6*f9fbec18Smcpowers * 1.1 (the "License"); you may not use this file except in compliance with 7*f9fbec18Smcpowers * the License. You may obtain a copy of the License at 8*f9fbec18Smcpowers * http://www.mozilla.org/MPL/ 9*f9fbec18Smcpowers * 10*f9fbec18Smcpowers * Software distributed under the License is distributed on an "AS IS" basis, 11*f9fbec18Smcpowers * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12*f9fbec18Smcpowers * for the specific language governing rights and limitations under the 13*f9fbec18Smcpowers * License. 14*f9fbec18Smcpowers * 15*f9fbec18Smcpowers * The Original Code is the Elliptic Curve Cryptography library. 16*f9fbec18Smcpowers * 17*f9fbec18Smcpowers * The Initial Developer of the Original Code is 18*f9fbec18Smcpowers * Sun Microsystems, Inc. 19*f9fbec18Smcpowers * Portions created by the Initial Developer are Copyright (C) 2003 20*f9fbec18Smcpowers * the Initial Developer. All Rights Reserved. 21*f9fbec18Smcpowers * 22*f9fbec18Smcpowers * Contributor(s): 23*f9fbec18Smcpowers * Dr Vipul Gupta <vipul.gupta@sun.com> and 24*f9fbec18Smcpowers * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories 25*f9fbec18Smcpowers * 26*f9fbec18Smcpowers * Alternatively, the contents of this file may be used under the terms of 27*f9fbec18Smcpowers * either the GNU General Public License Version 2 or later (the "GPL"), or 28*f9fbec18Smcpowers * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 29*f9fbec18Smcpowers * in which case the provisions of the GPL or the LGPL are applicable instead 30*f9fbec18Smcpowers * of those above. If you wish to allow use of your version of this file only 31*f9fbec18Smcpowers * under the terms of either the GPL or the LGPL, and not to allow others to 32*f9fbec18Smcpowers * use your version of this file under the terms of the MPL, indicate your 33*f9fbec18Smcpowers * decision by deleting the provisions above and replace them with the notice 34*f9fbec18Smcpowers * and other provisions required by the GPL or the LGPL. If you do not delete 35*f9fbec18Smcpowers * the provisions above, a recipient may use your version of this file under 36*f9fbec18Smcpowers * the terms of any one of the MPL, the GPL or the LGPL. 37*f9fbec18Smcpowers * 38*f9fbec18Smcpowers * ***** END LICENSE BLOCK ***** */ 39*f9fbec18Smcpowers /* 40*f9fbec18Smcpowers * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 41*f9fbec18Smcpowers * Use is subject to license terms. 42*f9fbec18Smcpowers * 43*f9fbec18Smcpowers * Sun elects to use this software under the MPL license. 44*f9fbec18Smcpowers */ 45*f9fbec18Smcpowers 46*f9fbec18Smcpowers #pragma ident "%Z%%M% %I% %E% SMI" 47*f9fbec18Smcpowers 48*f9fbec18Smcpowers #include "mplogic.h" 49*f9fbec18Smcpowers #include "ec.h" 50*f9fbec18Smcpowers #include "ecl.h" 51*f9fbec18Smcpowers 52*f9fbec18Smcpowers #include <sys/types.h> 53*f9fbec18Smcpowers #ifndef _KERNEL 54*f9fbec18Smcpowers #include <stdlib.h> 55*f9fbec18Smcpowers #include <string.h> 56*f9fbec18Smcpowers #include <strings.h> 57*f9fbec18Smcpowers #endif 58*f9fbec18Smcpowers #include "ecl-exp.h" 59*f9fbec18Smcpowers #include "mpi.h" 60*f9fbec18Smcpowers #include "ecc_impl.h" 61*f9fbec18Smcpowers 62*f9fbec18Smcpowers #ifdef _KERNEL 63*f9fbec18Smcpowers #define PORT_ZFree(p, l) bzero((p), (l)); kmem_free((p), (l)) 64*f9fbec18Smcpowers #else 65*f9fbec18Smcpowers #define PORT_ZFree(p, l) bzero((p), (l)); free((p)) 66*f9fbec18Smcpowers #endif 67*f9fbec18Smcpowers 68*f9fbec18Smcpowers /* 69*f9fbec18Smcpowers * Returns true if pointP is the point at infinity, false otherwise 70*f9fbec18Smcpowers */ 71*f9fbec18Smcpowers PRBool 72*f9fbec18Smcpowers ec_point_at_infinity(SECItem *pointP) 73*f9fbec18Smcpowers { 74*f9fbec18Smcpowers unsigned int i; 75*f9fbec18Smcpowers 76*f9fbec18Smcpowers for (i = 1; i < pointP->len; i++) { 77*f9fbec18Smcpowers if (pointP->data[i] != 0x00) return PR_FALSE; 78*f9fbec18Smcpowers } 79*f9fbec18Smcpowers 80*f9fbec18Smcpowers return PR_TRUE; 81*f9fbec18Smcpowers } 82*f9fbec18Smcpowers 83*f9fbec18Smcpowers /* 84*f9fbec18Smcpowers * Computes scalar point multiplication pointQ = k1 * G + k2 * pointP for 85*f9fbec18Smcpowers * the curve whose parameters are encoded in params with base point G. 86*f9fbec18Smcpowers */ 87*f9fbec18Smcpowers SECStatus 88*f9fbec18Smcpowers ec_points_mul(const ECParams *params, const mp_int *k1, const mp_int *k2, 89*f9fbec18Smcpowers const SECItem *pointP, SECItem *pointQ, int kmflag) 90*f9fbec18Smcpowers { 91*f9fbec18Smcpowers mp_int Px, Py, Qx, Qy; 92*f9fbec18Smcpowers mp_int Gx, Gy, order, irreducible, a, b; 93*f9fbec18Smcpowers #if 0 /* currently don't support non-named curves */ 94*f9fbec18Smcpowers unsigned int irr_arr[5]; 95*f9fbec18Smcpowers #endif 96*f9fbec18Smcpowers ECGroup *group = NULL; 97*f9fbec18Smcpowers SECStatus rv = SECFailure; 98*f9fbec18Smcpowers mp_err err = MP_OKAY; 99*f9fbec18Smcpowers int len; 100*f9fbec18Smcpowers 101*f9fbec18Smcpowers #if EC_DEBUG 102*f9fbec18Smcpowers int i; 103*f9fbec18Smcpowers char mpstr[256]; 104*f9fbec18Smcpowers 105*f9fbec18Smcpowers printf("ec_points_mul: params [len=%d]:", params->DEREncoding.len); 106*f9fbec18Smcpowers for (i = 0; i < params->DEREncoding.len; i++) 107*f9fbec18Smcpowers printf("%02x:", params->DEREncoding.data[i]); 108*f9fbec18Smcpowers printf("\n"); 109*f9fbec18Smcpowers 110*f9fbec18Smcpowers if (k1 != NULL) { 111*f9fbec18Smcpowers mp_tohex(k1, mpstr); 112*f9fbec18Smcpowers printf("ec_points_mul: scalar k1: %s\n", mpstr); 113*f9fbec18Smcpowers mp_todecimal(k1, mpstr); 114*f9fbec18Smcpowers printf("ec_points_mul: scalar k1: %s (dec)\n", mpstr); 115*f9fbec18Smcpowers } 116*f9fbec18Smcpowers 117*f9fbec18Smcpowers if (k2 != NULL) { 118*f9fbec18Smcpowers mp_tohex(k2, mpstr); 119*f9fbec18Smcpowers printf("ec_points_mul: scalar k2: %s\n", mpstr); 120*f9fbec18Smcpowers mp_todecimal(k2, mpstr); 121*f9fbec18Smcpowers printf("ec_points_mul: scalar k2: %s (dec)\n", mpstr); 122*f9fbec18Smcpowers } 123*f9fbec18Smcpowers 124*f9fbec18Smcpowers if (pointP != NULL) { 125*f9fbec18Smcpowers printf("ec_points_mul: pointP [len=%d]:", pointP->len); 126*f9fbec18Smcpowers for (i = 0; i < pointP->len; i++) 127*f9fbec18Smcpowers printf("%02x:", pointP->data[i]); 128*f9fbec18Smcpowers printf("\n"); 129*f9fbec18Smcpowers } 130*f9fbec18Smcpowers #endif 131*f9fbec18Smcpowers 132*f9fbec18Smcpowers /* NOTE: We only support uncompressed points for now */ 133*f9fbec18Smcpowers len = (params->fieldID.size + 7) >> 3; 134*f9fbec18Smcpowers if (pointP != NULL) { 135*f9fbec18Smcpowers if ((pointP->data[0] != EC_POINT_FORM_UNCOMPRESSED) || 136*f9fbec18Smcpowers (pointP->len != (2 * len + 1))) { 137*f9fbec18Smcpowers return SECFailure; 138*f9fbec18Smcpowers }; 139*f9fbec18Smcpowers } 140*f9fbec18Smcpowers 141*f9fbec18Smcpowers MP_DIGITS(&Px) = 0; 142*f9fbec18Smcpowers MP_DIGITS(&Py) = 0; 143*f9fbec18Smcpowers MP_DIGITS(&Qx) = 0; 144*f9fbec18Smcpowers MP_DIGITS(&Qy) = 0; 145*f9fbec18Smcpowers MP_DIGITS(&Gx) = 0; 146*f9fbec18Smcpowers MP_DIGITS(&Gy) = 0; 147*f9fbec18Smcpowers MP_DIGITS(&order) = 0; 148*f9fbec18Smcpowers MP_DIGITS(&irreducible) = 0; 149*f9fbec18Smcpowers MP_DIGITS(&a) = 0; 150*f9fbec18Smcpowers MP_DIGITS(&b) = 0; 151*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Px, kmflag) ); 152*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Py, kmflag) ); 153*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Qx, kmflag) ); 154*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Qy, kmflag) ); 155*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Gx, kmflag) ); 156*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Gy, kmflag) ); 157*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&order, kmflag) ); 158*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&irreducible, kmflag) ); 159*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&a, kmflag) ); 160*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&b, kmflag) ); 161*f9fbec18Smcpowers 162*f9fbec18Smcpowers if ((k2 != NULL) && (pointP != NULL)) { 163*f9fbec18Smcpowers /* Initialize Px and Py */ 164*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&Px, pointP->data + 1, (mp_size) len) ); 165*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&Py, pointP->data + 1 + len, (mp_size) len) ); 166*f9fbec18Smcpowers } 167*f9fbec18Smcpowers 168*f9fbec18Smcpowers /* construct from named params, if possible */ 169*f9fbec18Smcpowers if (params->name != ECCurve_noName) { 170*f9fbec18Smcpowers group = ECGroup_fromName(params->name, kmflag); 171*f9fbec18Smcpowers } 172*f9fbec18Smcpowers 173*f9fbec18Smcpowers #if 0 /* currently don't support non-named curves */ 174*f9fbec18Smcpowers if (group == NULL) { 175*f9fbec18Smcpowers /* Set up mp_ints containing the curve coefficients */ 176*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&Gx, params->base.data + 1, 177*f9fbec18Smcpowers (mp_size) len) ); 178*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&Gy, params->base.data + 1 + len, 179*f9fbec18Smcpowers (mp_size) len) ); 180*f9fbec18Smcpowers SECITEM_TO_MPINT( params->order, &order ); 181*f9fbec18Smcpowers SECITEM_TO_MPINT( params->curve.a, &a ); 182*f9fbec18Smcpowers SECITEM_TO_MPINT( params->curve.b, &b ); 183*f9fbec18Smcpowers if (params->fieldID.type == ec_field_GFp) { 184*f9fbec18Smcpowers SECITEM_TO_MPINT( params->fieldID.u.prime, &irreducible ); 185*f9fbec18Smcpowers group = ECGroup_consGFp(&irreducible, &a, &b, &Gx, &Gy, &order, params->cofactor); 186*f9fbec18Smcpowers } else { 187*f9fbec18Smcpowers SECITEM_TO_MPINT( params->fieldID.u.poly, &irreducible ); 188*f9fbec18Smcpowers irr_arr[0] = params->fieldID.size; 189*f9fbec18Smcpowers irr_arr[1] = params->fieldID.k1; 190*f9fbec18Smcpowers irr_arr[2] = params->fieldID.k2; 191*f9fbec18Smcpowers irr_arr[3] = params->fieldID.k3; 192*f9fbec18Smcpowers irr_arr[4] = 0; 193*f9fbec18Smcpowers group = ECGroup_consGF2m(&irreducible, irr_arr, &a, &b, &Gx, &Gy, &order, params->cofactor); 194*f9fbec18Smcpowers } 195*f9fbec18Smcpowers } 196*f9fbec18Smcpowers #endif 197*f9fbec18Smcpowers if (group == NULL) 198*f9fbec18Smcpowers goto cleanup; 199*f9fbec18Smcpowers 200*f9fbec18Smcpowers if ((k2 != NULL) && (pointP != NULL)) { 201*f9fbec18Smcpowers CHECK_MPI_OK( ECPoints_mul(group, k1, k2, &Px, &Py, &Qx, &Qy) ); 202*f9fbec18Smcpowers } else { 203*f9fbec18Smcpowers CHECK_MPI_OK( ECPoints_mul(group, k1, NULL, NULL, NULL, &Qx, &Qy) ); 204*f9fbec18Smcpowers } 205*f9fbec18Smcpowers 206*f9fbec18Smcpowers /* Construct the SECItem representation of point Q */ 207*f9fbec18Smcpowers pointQ->data[0] = EC_POINT_FORM_UNCOMPRESSED; 208*f9fbec18Smcpowers CHECK_MPI_OK( mp_to_fixlen_octets(&Qx, pointQ->data + 1, 209*f9fbec18Smcpowers (mp_size) len) ); 210*f9fbec18Smcpowers CHECK_MPI_OK( mp_to_fixlen_octets(&Qy, pointQ->data + 1 + len, 211*f9fbec18Smcpowers (mp_size) len) ); 212*f9fbec18Smcpowers 213*f9fbec18Smcpowers rv = SECSuccess; 214*f9fbec18Smcpowers 215*f9fbec18Smcpowers #if EC_DEBUG 216*f9fbec18Smcpowers printf("ec_points_mul: pointQ [len=%d]:", pointQ->len); 217*f9fbec18Smcpowers for (i = 0; i < pointQ->len; i++) 218*f9fbec18Smcpowers printf("%02x:", pointQ->data[i]); 219*f9fbec18Smcpowers printf("\n"); 220*f9fbec18Smcpowers #endif 221*f9fbec18Smcpowers 222*f9fbec18Smcpowers cleanup: 223*f9fbec18Smcpowers ECGroup_free(group); 224*f9fbec18Smcpowers mp_clear(&Px); 225*f9fbec18Smcpowers mp_clear(&Py); 226*f9fbec18Smcpowers mp_clear(&Qx); 227*f9fbec18Smcpowers mp_clear(&Qy); 228*f9fbec18Smcpowers mp_clear(&Gx); 229*f9fbec18Smcpowers mp_clear(&Gy); 230*f9fbec18Smcpowers mp_clear(&order); 231*f9fbec18Smcpowers mp_clear(&irreducible); 232*f9fbec18Smcpowers mp_clear(&a); 233*f9fbec18Smcpowers mp_clear(&b); 234*f9fbec18Smcpowers if (err) { 235*f9fbec18Smcpowers MP_TO_SEC_ERROR(err); 236*f9fbec18Smcpowers rv = SECFailure; 237*f9fbec18Smcpowers } 238*f9fbec18Smcpowers 239*f9fbec18Smcpowers return rv; 240*f9fbec18Smcpowers } 241*f9fbec18Smcpowers 242*f9fbec18Smcpowers /* Generates a new EC key pair. The private key is a supplied 243*f9fbec18Smcpowers * value and the public key is the result of performing a scalar 244*f9fbec18Smcpowers * point multiplication of that value with the curve's base point. 245*f9fbec18Smcpowers */ 246*f9fbec18Smcpowers SECStatus 247*f9fbec18Smcpowers ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey, 248*f9fbec18Smcpowers const unsigned char *privKeyBytes, int privKeyLen, int kmflag) 249*f9fbec18Smcpowers { 250*f9fbec18Smcpowers SECStatus rv = SECFailure; 251*f9fbec18Smcpowers PRArenaPool *arena; 252*f9fbec18Smcpowers ECPrivateKey *key; 253*f9fbec18Smcpowers mp_int k; 254*f9fbec18Smcpowers mp_err err = MP_OKAY; 255*f9fbec18Smcpowers int len; 256*f9fbec18Smcpowers 257*f9fbec18Smcpowers #if EC_DEBUG 258*f9fbec18Smcpowers printf("ec_NewKey called\n"); 259*f9fbec18Smcpowers #endif 260*f9fbec18Smcpowers 261*f9fbec18Smcpowers int printf(); 262*f9fbec18Smcpowers if (!ecParams || !privKey || !privKeyBytes || (privKeyLen < 0)) { 263*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 264*f9fbec18Smcpowers return SECFailure; 265*f9fbec18Smcpowers } 266*f9fbec18Smcpowers 267*f9fbec18Smcpowers /* Initialize an arena for the EC key. */ 268*f9fbec18Smcpowers if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE))) 269*f9fbec18Smcpowers return SECFailure; 270*f9fbec18Smcpowers 271*f9fbec18Smcpowers key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey), 272*f9fbec18Smcpowers kmflag); 273*f9fbec18Smcpowers if (!key) { 274*f9fbec18Smcpowers PORT_FreeArena(arena, PR_TRUE); 275*f9fbec18Smcpowers return SECFailure; 276*f9fbec18Smcpowers } 277*f9fbec18Smcpowers 278*f9fbec18Smcpowers /* Set the version number (SEC 1 section C.4 says it should be 1) */ 279*f9fbec18Smcpowers SECITEM_AllocItem(arena, &key->version, 1, kmflag); 280*f9fbec18Smcpowers key->version.data[0] = 1; 281*f9fbec18Smcpowers 282*f9fbec18Smcpowers /* Copy all of the fields from the ECParams argument to the 283*f9fbec18Smcpowers * ECParams structure within the private key. 284*f9fbec18Smcpowers */ 285*f9fbec18Smcpowers key->ecParams.arena = arena; 286*f9fbec18Smcpowers key->ecParams.type = ecParams->type; 287*f9fbec18Smcpowers key->ecParams.fieldID.size = ecParams->fieldID.size; 288*f9fbec18Smcpowers key->ecParams.fieldID.type = ecParams->fieldID.type; 289*f9fbec18Smcpowers if (ecParams->fieldID.type == ec_field_GFp) { 290*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.prime, 291*f9fbec18Smcpowers &ecParams->fieldID.u.prime, kmflag)); 292*f9fbec18Smcpowers } else { 293*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.poly, 294*f9fbec18Smcpowers &ecParams->fieldID.u.poly, kmflag)); 295*f9fbec18Smcpowers } 296*f9fbec18Smcpowers key->ecParams.fieldID.k1 = ecParams->fieldID.k1; 297*f9fbec18Smcpowers key->ecParams.fieldID.k2 = ecParams->fieldID.k2; 298*f9fbec18Smcpowers key->ecParams.fieldID.k3 = ecParams->fieldID.k3; 299*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.a, 300*f9fbec18Smcpowers &ecParams->curve.a, kmflag)); 301*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.b, 302*f9fbec18Smcpowers &ecParams->curve.b, kmflag)); 303*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.seed, 304*f9fbec18Smcpowers &ecParams->curve.seed, kmflag)); 305*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.base, 306*f9fbec18Smcpowers &ecParams->base, kmflag)); 307*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.order, 308*f9fbec18Smcpowers &ecParams->order, kmflag)); 309*f9fbec18Smcpowers key->ecParams.cofactor = ecParams->cofactor; 310*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.DEREncoding, 311*f9fbec18Smcpowers &ecParams->DEREncoding, kmflag)); 312*f9fbec18Smcpowers key->ecParams.name = ecParams->name; 313*f9fbec18Smcpowers CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curveOID, 314*f9fbec18Smcpowers &ecParams->curveOID, kmflag)); 315*f9fbec18Smcpowers 316*f9fbec18Smcpowers len = (ecParams->fieldID.size + 7) >> 3; 317*f9fbec18Smcpowers SECITEM_AllocItem(arena, &key->publicValue, 2*len + 1, kmflag); 318*f9fbec18Smcpowers len = ecParams->order.len; 319*f9fbec18Smcpowers SECITEM_AllocItem(arena, &key->privateValue, len, kmflag); 320*f9fbec18Smcpowers 321*f9fbec18Smcpowers /* Copy private key */ 322*f9fbec18Smcpowers if (privKeyLen >= len) { 323*f9fbec18Smcpowers memcpy(key->privateValue.data, privKeyBytes, len); 324*f9fbec18Smcpowers } else { 325*f9fbec18Smcpowers memset(key->privateValue.data, 0, (len - privKeyLen)); 326*f9fbec18Smcpowers memcpy(key->privateValue.data + (len - privKeyLen), privKeyBytes, privKeyLen); 327*f9fbec18Smcpowers } 328*f9fbec18Smcpowers 329*f9fbec18Smcpowers /* Compute corresponding public key */ 330*f9fbec18Smcpowers MP_DIGITS(&k) = 0; 331*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&k, kmflag) ); 332*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&k, key->privateValue.data, 333*f9fbec18Smcpowers (mp_size) len) ); 334*f9fbec18Smcpowers 335*f9fbec18Smcpowers rv = ec_points_mul(ecParams, &k, NULL, NULL, &(key->publicValue), kmflag); 336*f9fbec18Smcpowers if (rv != SECSuccess) goto cleanup; 337*f9fbec18Smcpowers *privKey = key; 338*f9fbec18Smcpowers 339*f9fbec18Smcpowers cleanup: 340*f9fbec18Smcpowers mp_clear(&k); 341*f9fbec18Smcpowers if (rv) 342*f9fbec18Smcpowers PORT_FreeArena(arena, PR_TRUE); 343*f9fbec18Smcpowers 344*f9fbec18Smcpowers #if EC_DEBUG 345*f9fbec18Smcpowers printf("ec_NewKey returning %s\n", 346*f9fbec18Smcpowers (rv == SECSuccess) ? "success" : "failure"); 347*f9fbec18Smcpowers #endif 348*f9fbec18Smcpowers 349*f9fbec18Smcpowers return rv; 350*f9fbec18Smcpowers 351*f9fbec18Smcpowers } 352*f9fbec18Smcpowers 353*f9fbec18Smcpowers /* Generates a new EC key pair. The private key is a supplied 354*f9fbec18Smcpowers * random value (in seed) and the public key is the result of 355*f9fbec18Smcpowers * performing a scalar point multiplication of that value with 356*f9fbec18Smcpowers * the curve's base point. 357*f9fbec18Smcpowers */ 358*f9fbec18Smcpowers SECStatus 359*f9fbec18Smcpowers EC_NewKeyFromSeed(ECParams *ecParams, ECPrivateKey **privKey, 360*f9fbec18Smcpowers const unsigned char *seed, int seedlen, int kmflag) 361*f9fbec18Smcpowers { 362*f9fbec18Smcpowers SECStatus rv = SECFailure; 363*f9fbec18Smcpowers rv = ec_NewKey(ecParams, privKey, seed, seedlen, kmflag); 364*f9fbec18Smcpowers return rv; 365*f9fbec18Smcpowers } 366*f9fbec18Smcpowers 367*f9fbec18Smcpowers /* Generate a random private key using the algorithm A.4.1 of ANSI X9.62, 368*f9fbec18Smcpowers * modified a la FIPS 186-2 Change Notice 1 to eliminate the bias in the 369*f9fbec18Smcpowers * random number generator. 370*f9fbec18Smcpowers * 371*f9fbec18Smcpowers * Parameters 372*f9fbec18Smcpowers * - order: a buffer that holds the curve's group order 373*f9fbec18Smcpowers * - len: the length in octets of the order buffer 374*f9fbec18Smcpowers * 375*f9fbec18Smcpowers * Return Value 376*f9fbec18Smcpowers * Returns a buffer of len octets that holds the private key. The caller 377*f9fbec18Smcpowers * is responsible for freeing the buffer with PORT_ZFree. 378*f9fbec18Smcpowers */ 379*f9fbec18Smcpowers static unsigned char * 380*f9fbec18Smcpowers ec_GenerateRandomPrivateKey(const unsigned char *order, int len, int kmflag) 381*f9fbec18Smcpowers { 382*f9fbec18Smcpowers SECStatus rv = SECSuccess; 383*f9fbec18Smcpowers mp_err err; 384*f9fbec18Smcpowers unsigned char *privKeyBytes = NULL; 385*f9fbec18Smcpowers mp_int privKeyVal, order_1, one; 386*f9fbec18Smcpowers 387*f9fbec18Smcpowers MP_DIGITS(&privKeyVal) = 0; 388*f9fbec18Smcpowers MP_DIGITS(&order_1) = 0; 389*f9fbec18Smcpowers MP_DIGITS(&one) = 0; 390*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&privKeyVal, kmflag) ); 391*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&order_1, kmflag) ); 392*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&one, kmflag) ); 393*f9fbec18Smcpowers 394*f9fbec18Smcpowers /* Generates 2*len random bytes using the global random bit generator 395*f9fbec18Smcpowers * (which implements Algorithm 1 of FIPS 186-2 Change Notice 1) then 396*f9fbec18Smcpowers * reduces modulo the group order. 397*f9fbec18Smcpowers */ 398*f9fbec18Smcpowers if ((privKeyBytes = PORT_Alloc(2*len, kmflag)) == NULL) goto cleanup; 399*f9fbec18Smcpowers CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(privKeyBytes, 2*len) ); 400*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&privKeyVal, privKeyBytes, 2*len) ); 401*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&order_1, order, len) ); 402*f9fbec18Smcpowers CHECK_MPI_OK( mp_set_int(&one, 1) ); 403*f9fbec18Smcpowers CHECK_MPI_OK( mp_sub(&order_1, &one, &order_1) ); 404*f9fbec18Smcpowers CHECK_MPI_OK( mp_mod(&privKeyVal, &order_1, &privKeyVal) ); 405*f9fbec18Smcpowers CHECK_MPI_OK( mp_add(&privKeyVal, &one, &privKeyVal) ); 406*f9fbec18Smcpowers CHECK_MPI_OK( mp_to_fixlen_octets(&privKeyVal, privKeyBytes, len) ); 407*f9fbec18Smcpowers memset(privKeyBytes+len, 0, len); 408*f9fbec18Smcpowers cleanup: 409*f9fbec18Smcpowers mp_clear(&privKeyVal); 410*f9fbec18Smcpowers mp_clear(&order_1); 411*f9fbec18Smcpowers mp_clear(&one); 412*f9fbec18Smcpowers if (err < MP_OKAY) { 413*f9fbec18Smcpowers MP_TO_SEC_ERROR(err); 414*f9fbec18Smcpowers rv = SECFailure; 415*f9fbec18Smcpowers } 416*f9fbec18Smcpowers if (rv != SECSuccess && privKeyBytes) { 417*f9fbec18Smcpowers #ifdef _KERNEL 418*f9fbec18Smcpowers kmem_free(privKeyBytes, 2*len); 419*f9fbec18Smcpowers #else 420*f9fbec18Smcpowers free(privKeyBytes); 421*f9fbec18Smcpowers #endif 422*f9fbec18Smcpowers privKeyBytes = NULL; 423*f9fbec18Smcpowers } 424*f9fbec18Smcpowers return privKeyBytes; 425*f9fbec18Smcpowers } 426*f9fbec18Smcpowers 427*f9fbec18Smcpowers /* Generates a new EC key pair. The private key is a random value and 428*f9fbec18Smcpowers * the public key is the result of performing a scalar point multiplication 429*f9fbec18Smcpowers * of that value with the curve's base point. 430*f9fbec18Smcpowers */ 431*f9fbec18Smcpowers SECStatus 432*f9fbec18Smcpowers EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey, int kmflag) 433*f9fbec18Smcpowers { 434*f9fbec18Smcpowers SECStatus rv = SECFailure; 435*f9fbec18Smcpowers int len; 436*f9fbec18Smcpowers unsigned char *privKeyBytes = NULL; 437*f9fbec18Smcpowers 438*f9fbec18Smcpowers if (!ecParams) { 439*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 440*f9fbec18Smcpowers return SECFailure; 441*f9fbec18Smcpowers } 442*f9fbec18Smcpowers 443*f9fbec18Smcpowers len = ecParams->order.len; 444*f9fbec18Smcpowers privKeyBytes = ec_GenerateRandomPrivateKey(ecParams->order.data, len, 445*f9fbec18Smcpowers kmflag); 446*f9fbec18Smcpowers if (privKeyBytes == NULL) goto cleanup; 447*f9fbec18Smcpowers /* generate public key */ 448*f9fbec18Smcpowers CHECK_SEC_OK( ec_NewKey(ecParams, privKey, privKeyBytes, len, kmflag) ); 449*f9fbec18Smcpowers 450*f9fbec18Smcpowers cleanup: 451*f9fbec18Smcpowers if (privKeyBytes) { 452*f9fbec18Smcpowers PORT_ZFree(privKeyBytes, len * 2); 453*f9fbec18Smcpowers } 454*f9fbec18Smcpowers #if EC_DEBUG 455*f9fbec18Smcpowers printf("EC_NewKey returning %s\n", 456*f9fbec18Smcpowers (rv == SECSuccess) ? "success" : "failure"); 457*f9fbec18Smcpowers #endif 458*f9fbec18Smcpowers 459*f9fbec18Smcpowers return rv; 460*f9fbec18Smcpowers } 461*f9fbec18Smcpowers 462*f9fbec18Smcpowers /* Validates an EC public key as described in Section 5.2.2 of 463*f9fbec18Smcpowers * X9.62. The ECDH primitive when used without the cofactor does 464*f9fbec18Smcpowers * not address small subgroup attacks, which may occur when the 465*f9fbec18Smcpowers * public key is not valid. These attacks can be prevented by 466*f9fbec18Smcpowers * validating the public key before using ECDH. 467*f9fbec18Smcpowers */ 468*f9fbec18Smcpowers SECStatus 469*f9fbec18Smcpowers EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue, int kmflag) 470*f9fbec18Smcpowers { 471*f9fbec18Smcpowers mp_int Px, Py; 472*f9fbec18Smcpowers ECGroup *group = NULL; 473*f9fbec18Smcpowers SECStatus rv = SECFailure; 474*f9fbec18Smcpowers mp_err err = MP_OKAY; 475*f9fbec18Smcpowers int len; 476*f9fbec18Smcpowers 477*f9fbec18Smcpowers if (!ecParams || !publicValue) { 478*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 479*f9fbec18Smcpowers return SECFailure; 480*f9fbec18Smcpowers } 481*f9fbec18Smcpowers 482*f9fbec18Smcpowers /* NOTE: We only support uncompressed points for now */ 483*f9fbec18Smcpowers len = (ecParams->fieldID.size + 7) >> 3; 484*f9fbec18Smcpowers if (publicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) { 485*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); 486*f9fbec18Smcpowers return SECFailure; 487*f9fbec18Smcpowers } else if (publicValue->len != (2 * len + 1)) { 488*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_BAD_KEY); 489*f9fbec18Smcpowers return SECFailure; 490*f9fbec18Smcpowers } 491*f9fbec18Smcpowers 492*f9fbec18Smcpowers MP_DIGITS(&Px) = 0; 493*f9fbec18Smcpowers MP_DIGITS(&Py) = 0; 494*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Px, kmflag) ); 495*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&Py, kmflag) ); 496*f9fbec18Smcpowers 497*f9fbec18Smcpowers /* Initialize Px and Py */ 498*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&Px, publicValue->data + 1, (mp_size) len) ); 499*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&Py, publicValue->data + 1 + len, (mp_size) len) ); 500*f9fbec18Smcpowers 501*f9fbec18Smcpowers /* construct from named params */ 502*f9fbec18Smcpowers group = ECGroup_fromName(ecParams->name, kmflag); 503*f9fbec18Smcpowers if (group == NULL) { 504*f9fbec18Smcpowers /* 505*f9fbec18Smcpowers * ECGroup_fromName fails if ecParams->name is not a valid 506*f9fbec18Smcpowers * ECCurveName value, or if we run out of memory, or perhaps 507*f9fbec18Smcpowers * for other reasons. Unfortunately if ecParams->name is a 508*f9fbec18Smcpowers * valid ECCurveName value, we don't know what the right error 509*f9fbec18Smcpowers * code should be because ECGroup_fromName doesn't return an 510*f9fbec18Smcpowers * error code to the caller. Set err to MP_UNDEF because 511*f9fbec18Smcpowers * that's what ECGroup_fromName uses internally. 512*f9fbec18Smcpowers */ 513*f9fbec18Smcpowers if ((ecParams->name <= ECCurve_noName) || 514*f9fbec18Smcpowers (ecParams->name >= ECCurve_pastLastCurve)) { 515*f9fbec18Smcpowers err = MP_BADARG; 516*f9fbec18Smcpowers } else { 517*f9fbec18Smcpowers err = MP_UNDEF; 518*f9fbec18Smcpowers } 519*f9fbec18Smcpowers goto cleanup; 520*f9fbec18Smcpowers } 521*f9fbec18Smcpowers 522*f9fbec18Smcpowers /* validate public point */ 523*f9fbec18Smcpowers if ((err = ECPoint_validate(group, &Px, &Py)) < MP_YES) { 524*f9fbec18Smcpowers if (err == MP_NO) { 525*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_BAD_KEY); 526*f9fbec18Smcpowers rv = SECFailure; 527*f9fbec18Smcpowers err = MP_OKAY; /* don't change the error code */ 528*f9fbec18Smcpowers } 529*f9fbec18Smcpowers goto cleanup; 530*f9fbec18Smcpowers } 531*f9fbec18Smcpowers 532*f9fbec18Smcpowers rv = SECSuccess; 533*f9fbec18Smcpowers 534*f9fbec18Smcpowers cleanup: 535*f9fbec18Smcpowers ECGroup_free(group); 536*f9fbec18Smcpowers mp_clear(&Px); 537*f9fbec18Smcpowers mp_clear(&Py); 538*f9fbec18Smcpowers if (err) { 539*f9fbec18Smcpowers MP_TO_SEC_ERROR(err); 540*f9fbec18Smcpowers rv = SECFailure; 541*f9fbec18Smcpowers } 542*f9fbec18Smcpowers return rv; 543*f9fbec18Smcpowers } 544*f9fbec18Smcpowers 545*f9fbec18Smcpowers /* 546*f9fbec18Smcpowers ** Performs an ECDH key derivation by computing the scalar point 547*f9fbec18Smcpowers ** multiplication of privateValue and publicValue (with or without the 548*f9fbec18Smcpowers ** cofactor) and returns the x-coordinate of the resulting elliptic 549*f9fbec18Smcpowers ** curve point in derived secret. If successful, derivedSecret->data 550*f9fbec18Smcpowers ** is set to the address of the newly allocated buffer containing the 551*f9fbec18Smcpowers ** derived secret, and derivedSecret->len is the size of the secret 552*f9fbec18Smcpowers ** produced. It is the caller's responsibility to free the allocated 553*f9fbec18Smcpowers ** buffer containing the derived secret. 554*f9fbec18Smcpowers */ 555*f9fbec18Smcpowers SECStatus 556*f9fbec18Smcpowers ECDH_Derive(SECItem *publicValue, 557*f9fbec18Smcpowers ECParams *ecParams, 558*f9fbec18Smcpowers SECItem *privateValue, 559*f9fbec18Smcpowers PRBool withCofactor, 560*f9fbec18Smcpowers SECItem *derivedSecret, 561*f9fbec18Smcpowers int kmflag) 562*f9fbec18Smcpowers { 563*f9fbec18Smcpowers SECStatus rv = SECFailure; 564*f9fbec18Smcpowers unsigned int len = 0; 565*f9fbec18Smcpowers SECItem pointQ = {siBuffer, NULL, 0}; 566*f9fbec18Smcpowers mp_int k; /* to hold the private value */ 567*f9fbec18Smcpowers mp_int cofactor; 568*f9fbec18Smcpowers mp_err err = MP_OKAY; 569*f9fbec18Smcpowers #if EC_DEBUG 570*f9fbec18Smcpowers int i; 571*f9fbec18Smcpowers #endif 572*f9fbec18Smcpowers 573*f9fbec18Smcpowers if (!publicValue || !ecParams || !privateValue || 574*f9fbec18Smcpowers !derivedSecret) { 575*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 576*f9fbec18Smcpowers return SECFailure; 577*f9fbec18Smcpowers } 578*f9fbec18Smcpowers 579*f9fbec18Smcpowers memset(derivedSecret, 0, sizeof *derivedSecret); 580*f9fbec18Smcpowers len = (ecParams->fieldID.size + 7) >> 3; 581*f9fbec18Smcpowers pointQ.len = 2*len + 1; 582*f9fbec18Smcpowers if ((pointQ.data = PORT_Alloc(2*len + 1, kmflag)) == NULL) goto cleanup; 583*f9fbec18Smcpowers 584*f9fbec18Smcpowers MP_DIGITS(&k) = 0; 585*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&k, kmflag) ); 586*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&k, privateValue->data, 587*f9fbec18Smcpowers (mp_size) privateValue->len) ); 588*f9fbec18Smcpowers 589*f9fbec18Smcpowers if (withCofactor && (ecParams->cofactor != 1)) { 590*f9fbec18Smcpowers /* multiply k with the cofactor */ 591*f9fbec18Smcpowers MP_DIGITS(&cofactor) = 0; 592*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&cofactor, kmflag) ); 593*f9fbec18Smcpowers mp_set(&cofactor, ecParams->cofactor); 594*f9fbec18Smcpowers CHECK_MPI_OK( mp_mul(&k, &cofactor, &k) ); 595*f9fbec18Smcpowers } 596*f9fbec18Smcpowers 597*f9fbec18Smcpowers /* Multiply our private key and peer's public point */ 598*f9fbec18Smcpowers if ((ec_points_mul(ecParams, NULL, &k, publicValue, &pointQ, kmflag) != SECSuccess) || 599*f9fbec18Smcpowers ec_point_at_infinity(&pointQ)) 600*f9fbec18Smcpowers goto cleanup; 601*f9fbec18Smcpowers 602*f9fbec18Smcpowers /* Allocate memory for the derived secret and copy 603*f9fbec18Smcpowers * the x co-ordinate of pointQ into it. 604*f9fbec18Smcpowers */ 605*f9fbec18Smcpowers SECITEM_AllocItem(NULL, derivedSecret, len, kmflag); 606*f9fbec18Smcpowers memcpy(derivedSecret->data, pointQ.data + 1, len); 607*f9fbec18Smcpowers 608*f9fbec18Smcpowers rv = SECSuccess; 609*f9fbec18Smcpowers 610*f9fbec18Smcpowers #if EC_DEBUG 611*f9fbec18Smcpowers printf("derived_secret:\n"); 612*f9fbec18Smcpowers for (i = 0; i < derivedSecret->len; i++) 613*f9fbec18Smcpowers printf("%02x:", derivedSecret->data[i]); 614*f9fbec18Smcpowers printf("\n"); 615*f9fbec18Smcpowers #endif 616*f9fbec18Smcpowers 617*f9fbec18Smcpowers cleanup: 618*f9fbec18Smcpowers mp_clear(&k); 619*f9fbec18Smcpowers 620*f9fbec18Smcpowers if (pointQ.data) { 621*f9fbec18Smcpowers PORT_ZFree(pointQ.data, 2*len + 1); 622*f9fbec18Smcpowers } 623*f9fbec18Smcpowers 624*f9fbec18Smcpowers return rv; 625*f9fbec18Smcpowers } 626*f9fbec18Smcpowers 627*f9fbec18Smcpowers /* Computes the ECDSA signature (a concatenation of two values r and s) 628*f9fbec18Smcpowers * on the digest using the given key and the random value kb (used in 629*f9fbec18Smcpowers * computing s). 630*f9fbec18Smcpowers */ 631*f9fbec18Smcpowers SECStatus 632*f9fbec18Smcpowers ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, 633*f9fbec18Smcpowers const SECItem *digest, const unsigned char *kb, const int kblen, int kmflag) 634*f9fbec18Smcpowers { 635*f9fbec18Smcpowers SECStatus rv = SECFailure; 636*f9fbec18Smcpowers mp_int x1; 637*f9fbec18Smcpowers mp_int d, k; /* private key, random integer */ 638*f9fbec18Smcpowers mp_int r, s; /* tuple (r, s) is the signature */ 639*f9fbec18Smcpowers mp_int n; 640*f9fbec18Smcpowers mp_err err = MP_OKAY; 641*f9fbec18Smcpowers ECParams *ecParams = NULL; 642*f9fbec18Smcpowers SECItem kGpoint = { siBuffer, NULL, 0}; 643*f9fbec18Smcpowers int flen = 0; /* length in bytes of the field size */ 644*f9fbec18Smcpowers unsigned olen; /* length in bytes of the base point order */ 645*f9fbec18Smcpowers 646*f9fbec18Smcpowers #if EC_DEBUG 647*f9fbec18Smcpowers char mpstr[256]; 648*f9fbec18Smcpowers #endif 649*f9fbec18Smcpowers 650*f9fbec18Smcpowers /* Initialize MPI integers. */ 651*f9fbec18Smcpowers /* must happen before the first potential call to cleanup */ 652*f9fbec18Smcpowers MP_DIGITS(&x1) = 0; 653*f9fbec18Smcpowers MP_DIGITS(&d) = 0; 654*f9fbec18Smcpowers MP_DIGITS(&k) = 0; 655*f9fbec18Smcpowers MP_DIGITS(&r) = 0; 656*f9fbec18Smcpowers MP_DIGITS(&s) = 0; 657*f9fbec18Smcpowers MP_DIGITS(&n) = 0; 658*f9fbec18Smcpowers 659*f9fbec18Smcpowers /* Check args */ 660*f9fbec18Smcpowers if (!key || !signature || !digest || !kb || (kblen < 0)) { 661*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 662*f9fbec18Smcpowers goto cleanup; 663*f9fbec18Smcpowers } 664*f9fbec18Smcpowers 665*f9fbec18Smcpowers ecParams = &(key->ecParams); 666*f9fbec18Smcpowers flen = (ecParams->fieldID.size + 7) >> 3; 667*f9fbec18Smcpowers olen = ecParams->order.len; 668*f9fbec18Smcpowers if (signature->data == NULL) { 669*f9fbec18Smcpowers /* a call to get the signature length only */ 670*f9fbec18Smcpowers goto finish; 671*f9fbec18Smcpowers } 672*f9fbec18Smcpowers if (signature->len < 2*olen) { 673*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_OUTPUT_LEN); 674*f9fbec18Smcpowers rv = SECBufferTooSmall; 675*f9fbec18Smcpowers goto cleanup; 676*f9fbec18Smcpowers } 677*f9fbec18Smcpowers 678*f9fbec18Smcpowers 679*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&x1, kmflag) ); 680*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&d, kmflag) ); 681*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&k, kmflag) ); 682*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&r, kmflag) ); 683*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&s, kmflag) ); 684*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&n, kmflag) ); 685*f9fbec18Smcpowers 686*f9fbec18Smcpowers SECITEM_TO_MPINT( ecParams->order, &n ); 687*f9fbec18Smcpowers SECITEM_TO_MPINT( key->privateValue, &d ); 688*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&k, kb, kblen) ); 689*f9fbec18Smcpowers /* Make sure k is in the interval [1, n-1] */ 690*f9fbec18Smcpowers if ((mp_cmp_z(&k) <= 0) || (mp_cmp(&k, &n) >= 0)) { 691*f9fbec18Smcpowers #if EC_DEBUG 692*f9fbec18Smcpowers printf("k is outside [1, n-1]\n"); 693*f9fbec18Smcpowers mp_tohex(&k, mpstr); 694*f9fbec18Smcpowers printf("k : %s \n", mpstr); 695*f9fbec18Smcpowers mp_tohex(&n, mpstr); 696*f9fbec18Smcpowers printf("n : %s \n", mpstr); 697*f9fbec18Smcpowers #endif 698*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_NEED_RANDOM); 699*f9fbec18Smcpowers goto cleanup; 700*f9fbec18Smcpowers } 701*f9fbec18Smcpowers 702*f9fbec18Smcpowers /* 703*f9fbec18Smcpowers ** ANSI X9.62, Section 5.3.2, Step 2 704*f9fbec18Smcpowers ** 705*f9fbec18Smcpowers ** Compute kG 706*f9fbec18Smcpowers */ 707*f9fbec18Smcpowers kGpoint.len = 2*flen + 1; 708*f9fbec18Smcpowers kGpoint.data = PORT_Alloc(2*flen + 1, kmflag); 709*f9fbec18Smcpowers if ((kGpoint.data == NULL) || 710*f9fbec18Smcpowers (ec_points_mul(ecParams, &k, NULL, NULL, &kGpoint, kmflag) 711*f9fbec18Smcpowers != SECSuccess)) 712*f9fbec18Smcpowers goto cleanup; 713*f9fbec18Smcpowers 714*f9fbec18Smcpowers /* 715*f9fbec18Smcpowers ** ANSI X9.62, Section 5.3.3, Step 1 716*f9fbec18Smcpowers ** 717*f9fbec18Smcpowers ** Extract the x co-ordinate of kG into x1 718*f9fbec18Smcpowers */ 719*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&x1, kGpoint.data + 1, 720*f9fbec18Smcpowers (mp_size) flen) ); 721*f9fbec18Smcpowers 722*f9fbec18Smcpowers /* 723*f9fbec18Smcpowers ** ANSI X9.62, Section 5.3.3, Step 2 724*f9fbec18Smcpowers ** 725*f9fbec18Smcpowers ** r = x1 mod n NOTE: n is the order of the curve 726*f9fbec18Smcpowers */ 727*f9fbec18Smcpowers CHECK_MPI_OK( mp_mod(&x1, &n, &r) ); 728*f9fbec18Smcpowers 729*f9fbec18Smcpowers /* 730*f9fbec18Smcpowers ** ANSI X9.62, Section 5.3.3, Step 3 731*f9fbec18Smcpowers ** 732*f9fbec18Smcpowers ** verify r != 0 733*f9fbec18Smcpowers */ 734*f9fbec18Smcpowers if (mp_cmp_z(&r) == 0) { 735*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_NEED_RANDOM); 736*f9fbec18Smcpowers goto cleanup; 737*f9fbec18Smcpowers } 738*f9fbec18Smcpowers 739*f9fbec18Smcpowers /* 740*f9fbec18Smcpowers ** ANSI X9.62, Section 5.3.3, Step 4 741*f9fbec18Smcpowers ** 742*f9fbec18Smcpowers ** s = (k**-1 * (HASH(M) + d*r)) mod n 743*f9fbec18Smcpowers */ 744*f9fbec18Smcpowers SECITEM_TO_MPINT(*digest, &s); /* s = HASH(M) */ 745*f9fbec18Smcpowers 746*f9fbec18Smcpowers /* In the definition of EC signing, digests are truncated 747*f9fbec18Smcpowers * to the length of n in bits. 748*f9fbec18Smcpowers * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ 749*f9fbec18Smcpowers if (digest->len*8 > ecParams->fieldID.size) { 750*f9fbec18Smcpowers mpl_rsh(&s,&s,digest->len*8 - ecParams->fieldID.size); 751*f9fbec18Smcpowers } 752*f9fbec18Smcpowers 753*f9fbec18Smcpowers #if EC_DEBUG 754*f9fbec18Smcpowers mp_todecimal(&n, mpstr); 755*f9fbec18Smcpowers printf("n : %s (dec)\n", mpstr); 756*f9fbec18Smcpowers mp_todecimal(&d, mpstr); 757*f9fbec18Smcpowers printf("d : %s (dec)\n", mpstr); 758*f9fbec18Smcpowers mp_tohex(&x1, mpstr); 759*f9fbec18Smcpowers printf("x1: %s\n", mpstr); 760*f9fbec18Smcpowers mp_todecimal(&s, mpstr); 761*f9fbec18Smcpowers printf("digest: %s (decimal)\n", mpstr); 762*f9fbec18Smcpowers mp_todecimal(&r, mpstr); 763*f9fbec18Smcpowers printf("r : %s (dec)\n", mpstr); 764*f9fbec18Smcpowers mp_tohex(&r, mpstr); 765*f9fbec18Smcpowers printf("r : %s\n", mpstr); 766*f9fbec18Smcpowers #endif 767*f9fbec18Smcpowers 768*f9fbec18Smcpowers CHECK_MPI_OK( mp_invmod(&k, &n, &k) ); /* k = k**-1 mod n */ 769*f9fbec18Smcpowers CHECK_MPI_OK( mp_mulmod(&d, &r, &n, &d) ); /* d = d * r mod n */ 770*f9fbec18Smcpowers CHECK_MPI_OK( mp_addmod(&s, &d, &n, &s) ); /* s = s + d mod n */ 771*f9fbec18Smcpowers CHECK_MPI_OK( mp_mulmod(&s, &k, &n, &s) ); /* s = s * k mod n */ 772*f9fbec18Smcpowers 773*f9fbec18Smcpowers #if EC_DEBUG 774*f9fbec18Smcpowers mp_todecimal(&s, mpstr); 775*f9fbec18Smcpowers printf("s : %s (dec)\n", mpstr); 776*f9fbec18Smcpowers mp_tohex(&s, mpstr); 777*f9fbec18Smcpowers printf("s : %s\n", mpstr); 778*f9fbec18Smcpowers #endif 779*f9fbec18Smcpowers 780*f9fbec18Smcpowers /* 781*f9fbec18Smcpowers ** ANSI X9.62, Section 5.3.3, Step 5 782*f9fbec18Smcpowers ** 783*f9fbec18Smcpowers ** verify s != 0 784*f9fbec18Smcpowers */ 785*f9fbec18Smcpowers if (mp_cmp_z(&s) == 0) { 786*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_NEED_RANDOM); 787*f9fbec18Smcpowers goto cleanup; 788*f9fbec18Smcpowers } 789*f9fbec18Smcpowers 790*f9fbec18Smcpowers /* 791*f9fbec18Smcpowers ** 792*f9fbec18Smcpowers ** Signature is tuple (r, s) 793*f9fbec18Smcpowers */ 794*f9fbec18Smcpowers CHECK_MPI_OK( mp_to_fixlen_octets(&r, signature->data, olen) ); 795*f9fbec18Smcpowers CHECK_MPI_OK( mp_to_fixlen_octets(&s, signature->data + olen, olen) ); 796*f9fbec18Smcpowers finish: 797*f9fbec18Smcpowers signature->len = 2*olen; 798*f9fbec18Smcpowers 799*f9fbec18Smcpowers rv = SECSuccess; 800*f9fbec18Smcpowers err = MP_OKAY; 801*f9fbec18Smcpowers cleanup: 802*f9fbec18Smcpowers mp_clear(&x1); 803*f9fbec18Smcpowers mp_clear(&d); 804*f9fbec18Smcpowers mp_clear(&k); 805*f9fbec18Smcpowers mp_clear(&r); 806*f9fbec18Smcpowers mp_clear(&s); 807*f9fbec18Smcpowers mp_clear(&n); 808*f9fbec18Smcpowers 809*f9fbec18Smcpowers if (kGpoint.data) { 810*f9fbec18Smcpowers PORT_ZFree(kGpoint.data, 2*flen + 1); 811*f9fbec18Smcpowers } 812*f9fbec18Smcpowers 813*f9fbec18Smcpowers if (err) { 814*f9fbec18Smcpowers MP_TO_SEC_ERROR(err); 815*f9fbec18Smcpowers rv = SECFailure; 816*f9fbec18Smcpowers } 817*f9fbec18Smcpowers 818*f9fbec18Smcpowers #if EC_DEBUG 819*f9fbec18Smcpowers printf("ECDSA signing with seed %s\n", 820*f9fbec18Smcpowers (rv == SECSuccess) ? "succeeded" : "failed"); 821*f9fbec18Smcpowers #endif 822*f9fbec18Smcpowers 823*f9fbec18Smcpowers return rv; 824*f9fbec18Smcpowers } 825*f9fbec18Smcpowers 826*f9fbec18Smcpowers /* 827*f9fbec18Smcpowers ** Computes the ECDSA signature on the digest using the given key 828*f9fbec18Smcpowers ** and a random seed. 829*f9fbec18Smcpowers */ 830*f9fbec18Smcpowers SECStatus 831*f9fbec18Smcpowers ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest, 832*f9fbec18Smcpowers int kmflag) 833*f9fbec18Smcpowers { 834*f9fbec18Smcpowers SECStatus rv = SECFailure; 835*f9fbec18Smcpowers int len; 836*f9fbec18Smcpowers unsigned char *kBytes= NULL; 837*f9fbec18Smcpowers 838*f9fbec18Smcpowers if (!key) { 839*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 840*f9fbec18Smcpowers return SECFailure; 841*f9fbec18Smcpowers } 842*f9fbec18Smcpowers 843*f9fbec18Smcpowers /* Generate random value k */ 844*f9fbec18Smcpowers len = key->ecParams.order.len; 845*f9fbec18Smcpowers kBytes = ec_GenerateRandomPrivateKey(key->ecParams.order.data, len, 846*f9fbec18Smcpowers kmflag); 847*f9fbec18Smcpowers if (kBytes == NULL) goto cleanup; 848*f9fbec18Smcpowers 849*f9fbec18Smcpowers /* Generate ECDSA signature with the specified k value */ 850*f9fbec18Smcpowers rv = ECDSA_SignDigestWithSeed(key, signature, digest, kBytes, len, kmflag); 851*f9fbec18Smcpowers 852*f9fbec18Smcpowers cleanup: 853*f9fbec18Smcpowers if (kBytes) { 854*f9fbec18Smcpowers PORT_ZFree(kBytes, len * 2); 855*f9fbec18Smcpowers } 856*f9fbec18Smcpowers 857*f9fbec18Smcpowers #if EC_DEBUG 858*f9fbec18Smcpowers printf("ECDSA signing %s\n", 859*f9fbec18Smcpowers (rv == SECSuccess) ? "succeeded" : "failed"); 860*f9fbec18Smcpowers #endif 861*f9fbec18Smcpowers 862*f9fbec18Smcpowers return rv; 863*f9fbec18Smcpowers } 864*f9fbec18Smcpowers 865*f9fbec18Smcpowers /* 866*f9fbec18Smcpowers ** Checks the signature on the given digest using the key provided. 867*f9fbec18Smcpowers */ 868*f9fbec18Smcpowers SECStatus 869*f9fbec18Smcpowers ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature, 870*f9fbec18Smcpowers const SECItem *digest, int kmflag) 871*f9fbec18Smcpowers { 872*f9fbec18Smcpowers SECStatus rv = SECFailure; 873*f9fbec18Smcpowers mp_int r_, s_; /* tuple (r', s') is received signature) */ 874*f9fbec18Smcpowers mp_int c, u1, u2, v; /* intermediate values used in verification */ 875*f9fbec18Smcpowers mp_int x1; 876*f9fbec18Smcpowers mp_int n; 877*f9fbec18Smcpowers mp_err err = MP_OKAY; 878*f9fbec18Smcpowers ECParams *ecParams = NULL; 879*f9fbec18Smcpowers SECItem pointC = { siBuffer, NULL, 0 }; 880*f9fbec18Smcpowers int slen; /* length in bytes of a half signature (r or s) */ 881*f9fbec18Smcpowers int flen; /* length in bytes of the field size */ 882*f9fbec18Smcpowers unsigned olen; /* length in bytes of the base point order */ 883*f9fbec18Smcpowers 884*f9fbec18Smcpowers #if EC_DEBUG 885*f9fbec18Smcpowers char mpstr[256]; 886*f9fbec18Smcpowers printf("ECDSA verification called\n"); 887*f9fbec18Smcpowers #endif 888*f9fbec18Smcpowers 889*f9fbec18Smcpowers /* Initialize MPI integers. */ 890*f9fbec18Smcpowers /* must happen before the first potential call to cleanup */ 891*f9fbec18Smcpowers MP_DIGITS(&r_) = 0; 892*f9fbec18Smcpowers MP_DIGITS(&s_) = 0; 893*f9fbec18Smcpowers MP_DIGITS(&c) = 0; 894*f9fbec18Smcpowers MP_DIGITS(&u1) = 0; 895*f9fbec18Smcpowers MP_DIGITS(&u2) = 0; 896*f9fbec18Smcpowers MP_DIGITS(&x1) = 0; 897*f9fbec18Smcpowers MP_DIGITS(&v) = 0; 898*f9fbec18Smcpowers MP_DIGITS(&n) = 0; 899*f9fbec18Smcpowers 900*f9fbec18Smcpowers /* Check args */ 901*f9fbec18Smcpowers if (!key || !signature || !digest) { 902*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INVALID_ARGS); 903*f9fbec18Smcpowers goto cleanup; 904*f9fbec18Smcpowers } 905*f9fbec18Smcpowers 906*f9fbec18Smcpowers ecParams = &(key->ecParams); 907*f9fbec18Smcpowers flen = (ecParams->fieldID.size + 7) >> 3; 908*f9fbec18Smcpowers olen = ecParams->order.len; 909*f9fbec18Smcpowers if (signature->len == 0 || signature->len%2 != 0 || 910*f9fbec18Smcpowers signature->len > 2*olen) { 911*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_INPUT_LEN); 912*f9fbec18Smcpowers goto cleanup; 913*f9fbec18Smcpowers } 914*f9fbec18Smcpowers slen = signature->len/2; 915*f9fbec18Smcpowers 916*f9fbec18Smcpowers SECITEM_AllocItem(NULL, &pointC, 2*flen + 1, kmflag); 917*f9fbec18Smcpowers if (pointC.data == NULL) 918*f9fbec18Smcpowers goto cleanup; 919*f9fbec18Smcpowers 920*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&r_, kmflag) ); 921*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&s_, kmflag) ); 922*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&c, kmflag) ); 923*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&u1, kmflag) ); 924*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&u2, kmflag) ); 925*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&x1, kmflag) ); 926*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&v, kmflag) ); 927*f9fbec18Smcpowers CHECK_MPI_OK( mp_init(&n, kmflag) ); 928*f9fbec18Smcpowers 929*f9fbec18Smcpowers /* 930*f9fbec18Smcpowers ** Convert received signature (r', s') into MPI integers. 931*f9fbec18Smcpowers */ 932*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&r_, signature->data, slen) ); 933*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&s_, signature->data + slen, slen) ); 934*f9fbec18Smcpowers 935*f9fbec18Smcpowers /* 936*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.2, Steps 1 and 2 937*f9fbec18Smcpowers ** 938*f9fbec18Smcpowers ** Verify that 0 < r' < n and 0 < s' < n 939*f9fbec18Smcpowers */ 940*f9fbec18Smcpowers SECITEM_TO_MPINT(ecParams->order, &n); 941*f9fbec18Smcpowers if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 || 942*f9fbec18Smcpowers mp_cmp(&r_, &n) >= 0 || mp_cmp(&s_, &n) >= 0) { 943*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_BAD_SIGNATURE); 944*f9fbec18Smcpowers goto cleanup; /* will return rv == SECFailure */ 945*f9fbec18Smcpowers } 946*f9fbec18Smcpowers 947*f9fbec18Smcpowers /* 948*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.2, Step 3 949*f9fbec18Smcpowers ** 950*f9fbec18Smcpowers ** c = (s')**-1 mod n 951*f9fbec18Smcpowers */ 952*f9fbec18Smcpowers CHECK_MPI_OK( mp_invmod(&s_, &n, &c) ); /* c = (s')**-1 mod n */ 953*f9fbec18Smcpowers 954*f9fbec18Smcpowers /* 955*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.2, Step 4 956*f9fbec18Smcpowers ** 957*f9fbec18Smcpowers ** u1 = ((HASH(M')) * c) mod n 958*f9fbec18Smcpowers */ 959*f9fbec18Smcpowers SECITEM_TO_MPINT(*digest, &u1); /* u1 = HASH(M) */ 960*f9fbec18Smcpowers 961*f9fbec18Smcpowers /* In the definition of EC signing, digests are truncated 962*f9fbec18Smcpowers * to the length of n in bits. 963*f9fbec18Smcpowers * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ 964*f9fbec18Smcpowers if (digest->len*8 > ecParams->fieldID.size) { /* u1 = HASH(M') */ 965*f9fbec18Smcpowers mpl_rsh(&u1,&u1,digest->len*8- ecParams->fieldID.size); 966*f9fbec18Smcpowers } 967*f9fbec18Smcpowers 968*f9fbec18Smcpowers #if EC_DEBUG 969*f9fbec18Smcpowers mp_todecimal(&r_, mpstr); 970*f9fbec18Smcpowers printf("r_: %s (dec)\n", mpstr); 971*f9fbec18Smcpowers mp_todecimal(&s_, mpstr); 972*f9fbec18Smcpowers printf("s_: %s (dec)\n", mpstr); 973*f9fbec18Smcpowers mp_todecimal(&c, mpstr); 974*f9fbec18Smcpowers printf("c : %s (dec)\n", mpstr); 975*f9fbec18Smcpowers mp_todecimal(&u1, mpstr); 976*f9fbec18Smcpowers printf("digest: %s (dec)\n", mpstr); 977*f9fbec18Smcpowers #endif 978*f9fbec18Smcpowers 979*f9fbec18Smcpowers CHECK_MPI_OK( mp_mulmod(&u1, &c, &n, &u1) ); /* u1 = u1 * c mod n */ 980*f9fbec18Smcpowers 981*f9fbec18Smcpowers /* 982*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.2, Step 4 983*f9fbec18Smcpowers ** 984*f9fbec18Smcpowers ** u2 = ((r') * c) mod n 985*f9fbec18Smcpowers */ 986*f9fbec18Smcpowers CHECK_MPI_OK( mp_mulmod(&r_, &c, &n, &u2) ); 987*f9fbec18Smcpowers 988*f9fbec18Smcpowers /* 989*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.3, Step 1 990*f9fbec18Smcpowers ** 991*f9fbec18Smcpowers ** Compute u1*G + u2*Q 992*f9fbec18Smcpowers ** Here, A = u1.G B = u2.Q and C = A + B 993*f9fbec18Smcpowers ** If the result, C, is the point at infinity, reject the signature 994*f9fbec18Smcpowers */ 995*f9fbec18Smcpowers if (ec_points_mul(ecParams, &u1, &u2, &key->publicValue, &pointC, kmflag) 996*f9fbec18Smcpowers != SECSuccess) { 997*f9fbec18Smcpowers rv = SECFailure; 998*f9fbec18Smcpowers goto cleanup; 999*f9fbec18Smcpowers } 1000*f9fbec18Smcpowers if (ec_point_at_infinity(&pointC)) { 1001*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_BAD_SIGNATURE); 1002*f9fbec18Smcpowers rv = SECFailure; 1003*f9fbec18Smcpowers goto cleanup; 1004*f9fbec18Smcpowers } 1005*f9fbec18Smcpowers 1006*f9fbec18Smcpowers CHECK_MPI_OK( mp_read_unsigned_octets(&x1, pointC.data + 1, flen) ); 1007*f9fbec18Smcpowers 1008*f9fbec18Smcpowers /* 1009*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.4, Step 2 1010*f9fbec18Smcpowers ** 1011*f9fbec18Smcpowers ** v = x1 mod n 1012*f9fbec18Smcpowers */ 1013*f9fbec18Smcpowers CHECK_MPI_OK( mp_mod(&x1, &n, &v) ); 1014*f9fbec18Smcpowers 1015*f9fbec18Smcpowers #if EC_DEBUG 1016*f9fbec18Smcpowers mp_todecimal(&r_, mpstr); 1017*f9fbec18Smcpowers printf("r_: %s (dec)\n", mpstr); 1018*f9fbec18Smcpowers mp_todecimal(&v, mpstr); 1019*f9fbec18Smcpowers printf("v : %s (dec)\n", mpstr); 1020*f9fbec18Smcpowers #endif 1021*f9fbec18Smcpowers 1022*f9fbec18Smcpowers /* 1023*f9fbec18Smcpowers ** ANSI X9.62, Section 5.4.4, Step 3 1024*f9fbec18Smcpowers ** 1025*f9fbec18Smcpowers ** Verification: v == r' 1026*f9fbec18Smcpowers */ 1027*f9fbec18Smcpowers if (mp_cmp(&v, &r_)) { 1028*f9fbec18Smcpowers PORT_SetError(SEC_ERROR_BAD_SIGNATURE); 1029*f9fbec18Smcpowers rv = SECFailure; /* Signature failed to verify. */ 1030*f9fbec18Smcpowers } else { 1031*f9fbec18Smcpowers rv = SECSuccess; /* Signature verified. */ 1032*f9fbec18Smcpowers } 1033*f9fbec18Smcpowers 1034*f9fbec18Smcpowers #if EC_DEBUG 1035*f9fbec18Smcpowers mp_todecimal(&u1, mpstr); 1036*f9fbec18Smcpowers printf("u1: %s (dec)\n", mpstr); 1037*f9fbec18Smcpowers mp_todecimal(&u2, mpstr); 1038*f9fbec18Smcpowers printf("u2: %s (dec)\n", mpstr); 1039*f9fbec18Smcpowers mp_tohex(&x1, mpstr); 1040*f9fbec18Smcpowers printf("x1: %s\n", mpstr); 1041*f9fbec18Smcpowers mp_todecimal(&v, mpstr); 1042*f9fbec18Smcpowers printf("v : %s (dec)\n", mpstr); 1043*f9fbec18Smcpowers #endif 1044*f9fbec18Smcpowers 1045*f9fbec18Smcpowers cleanup: 1046*f9fbec18Smcpowers mp_clear(&r_); 1047*f9fbec18Smcpowers mp_clear(&s_); 1048*f9fbec18Smcpowers mp_clear(&c); 1049*f9fbec18Smcpowers mp_clear(&u1); 1050*f9fbec18Smcpowers mp_clear(&u2); 1051*f9fbec18Smcpowers mp_clear(&x1); 1052*f9fbec18Smcpowers mp_clear(&v); 1053*f9fbec18Smcpowers mp_clear(&n); 1054*f9fbec18Smcpowers 1055*f9fbec18Smcpowers if (pointC.data) SECITEM_FreeItem(&pointC, PR_FALSE); 1056*f9fbec18Smcpowers if (err) { 1057*f9fbec18Smcpowers MP_TO_SEC_ERROR(err); 1058*f9fbec18Smcpowers rv = SECFailure; 1059*f9fbec18Smcpowers } 1060*f9fbec18Smcpowers 1061*f9fbec18Smcpowers #if EC_DEBUG 1062*f9fbec18Smcpowers printf("ECDSA verification %s\n", 1063*f9fbec18Smcpowers (rv == SECSuccess) ? "succeeded" : "failed"); 1064*f9fbec18Smcpowers #endif 1065*f9fbec18Smcpowers 1066*f9fbec18Smcpowers return rv; 1067*f9fbec18Smcpowers } 1068*f9fbec18Smcpowers 1069