security/manager/ssl/src/nsKeygenHandler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "secdert.h"
michael@0 8 #include "nspr.h"
michael@0 9 #include "nsNSSComponent.h" // for PIPNSS string bundle calls.
michael@0 10 #include "keyhi.h"
michael@0 11 #include "secder.h"
michael@0 12 #include "cryptohi.h"
michael@0 13 #include "base64.h"
michael@0 14 #include "secasn1.h"
michael@0 15 #include "pk11pqg.h"
michael@0 16 #include "nsKeygenHandler.h"
michael@0 17 #include "nsIServiceManager.h"
michael@0 18 #include "nsIDOMHTMLSelectElement.h"
michael@0 19 #include "nsIContent.h"
michael@0 20 #include "nsKeygenThread.h"
michael@0 21 #include "nsReadableUtils.h"
michael@0 22 #include "nsUnicharUtils.h"
michael@0 23 #include "nsCRT.h"
michael@0 24 #include "nsITokenDialogs.h"
michael@0 25 #include "nsIGenKeypairInfoDlg.h"
michael@0 26 #include "nsNSSShutDown.h"
michael@0 27
michael@0 28 //These defines are taken from the PKCS#11 spec
michael@0 29 #define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000
michael@0 30 #define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020
michael@0 31 #define CKM_DSA_KEY_PAIR_GEN 0x00000010
michael@0 32
michael@0 33 DERTemplate SECAlgorithmIDTemplate[] = {
michael@0 34 { DER_SEQUENCE,
michael@0 35 0, nullptr, sizeof(SECAlgorithmID) },
michael@0 36 { DER_OBJECT_ID,
michael@0 37 offsetof(SECAlgorithmID,algorithm), },
michael@0 38 { DER_OPTIONAL | DER_ANY,
michael@0 39 offsetof(SECAlgorithmID,parameters), },
michael@0 40 { 0, }
michael@0 41 };
michael@0 42
michael@0 43 DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
michael@0 44 { DER_SEQUENCE,
michael@0 45 0, nullptr, sizeof(CERTSubjectPublicKeyInfo) },
michael@0 46 { DER_INLINE,
michael@0 47 offsetof(CERTSubjectPublicKeyInfo,algorithm),
michael@0 48 SECAlgorithmIDTemplate, },
michael@0 49 { DER_BIT_STRING,
michael@0 50 offsetof(CERTSubjectPublicKeyInfo,subjectPublicKey), },
michael@0 51 { 0, }
michael@0 52 };
michael@0 53
michael@0 54 DERTemplate CERTPublicKeyAndChallengeTemplate[] =
michael@0 55 {
michael@0 56 { DER_SEQUENCE, 0, nullptr, sizeof(CERTPublicKeyAndChallenge) },
michael@0 57 { DER_ANY, offsetof(CERTPublicKeyAndChallenge,spki), },
michael@0 58 { DER_IA5_STRING, offsetof(CERTPublicKeyAndChallenge,challenge), },
michael@0 59 { 0, }
michael@0 60 };
michael@0 61
michael@0 62 const SEC_ASN1Template SECKEY_PQGParamsTemplate[] = {
michael@0 63 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(PQGParams) },
michael@0 64 { SEC_ASN1_INTEGER, offsetof(PQGParams,prime) },
michael@0 65 { SEC_ASN1_INTEGER, offsetof(PQGParams,subPrime) },
michael@0 66 { SEC_ASN1_INTEGER, offsetof(PQGParams,base) },
michael@0 67 { 0, }
michael@0 68 };
michael@0 69
michael@0 70
michael@0 71 static NS_DEFINE_IID(kIDOMHTMLSelectElementIID, NS_IDOMHTMLSELECTELEMENT_IID);
michael@0 72
michael@0 73 static PQGParams *
michael@0 74 decode_pqg_params(char *aStr)
michael@0 75 {
michael@0 76 unsigned char *buf = nullptr;
michael@0 77 unsigned int len;
michael@0 78 PLArenaPool *arena = nullptr;
michael@0 79 PQGParams *params = nullptr;
michael@0 80 SECStatus status;
michael@0 81
michael@0 82 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 83 if (!arena)
michael@0 84 return nullptr;
michael@0 85
michael@0 86 params = static_cast<PQGParams*>(PORT_ArenaZAlloc(arena, sizeof(PQGParams)));
michael@0 87 if (!params)
michael@0 88 goto loser;
michael@0 89 params->arena = arena;
michael@0 90
michael@0 91 buf = ATOB_AsciiToData(aStr, &len);
michael@0 92 if ((!buf) || (len == 0))
michael@0 93 goto loser;
michael@0 94
michael@0 95 status = SEC_ASN1Decode(arena, params, SECKEY_PQGParamsTemplate, (const char*)buf, len);
michael@0 96 if (status != SECSuccess)
michael@0 97 goto loser;
michael@0 98
michael@0 99 return params;
michael@0 100
michael@0 101 loser:
michael@0 102 if (arena) {
michael@0 103 PORT_FreeArena(arena, false);
michael@0 104 }
michael@0 105 if (buf) {
michael@0 106 PR_Free(buf);
michael@0 107 }
michael@0 108 return nullptr;
michael@0 109 }
michael@0 110
michael@0 111 static int
michael@0 112 pqg_prime_bits(char *str)
michael@0 113 {
michael@0 114 PQGParams *params = nullptr;
michael@0 115 int primeBits = 0, i;
michael@0 116
michael@0 117 params = decode_pqg_params(str);
michael@0 118 if (!params)
michael@0 119 goto done; /* lose */
michael@0 120
michael@0 121 for (i = 0; params->prime.data[i] == 0; i++)
michael@0 122 /* empty */;
michael@0 123 primeBits = (params->prime.len - i) * 8;
michael@0 124
michael@0 125 done:
michael@0 126 if (params)
michael@0 127 PK11_PQG_DestroyParams(params);
michael@0 128 return primeBits;
michael@0 129 }
michael@0 130
michael@0 131 typedef struct curveNameTagPairStr {
michael@0 132 const char *curveName;
michael@0 133 SECOidTag curveOidTag;
michael@0 134 } CurveNameTagPair;
michael@0 135
michael@0 136 static CurveNameTagPair nameTagPair[] =
michael@0 137 {
michael@0 138 { "prime192v1", SEC_OID_ANSIX962_EC_PRIME192V1 },
michael@0 139 { "prime192v2", SEC_OID_ANSIX962_EC_PRIME192V2 },
michael@0 140 { "prime192v3", SEC_OID_ANSIX962_EC_PRIME192V3 },
michael@0 141 { "prime239v1", SEC_OID_ANSIX962_EC_PRIME239V1 },
michael@0 142 { "prime239v2", SEC_OID_ANSIX962_EC_PRIME239V2 },
michael@0 143 { "prime239v3", SEC_OID_ANSIX962_EC_PRIME239V3 },
michael@0 144 { "prime256v1", SEC_OID_ANSIX962_EC_PRIME256V1 },
michael@0 145
michael@0 146 { "secp112r1", SEC_OID_SECG_EC_SECP112R1},
michael@0 147 { "secp112r2", SEC_OID_SECG_EC_SECP112R2},
michael@0 148 { "secp128r1", SEC_OID_SECG_EC_SECP128R1},
michael@0 149 { "secp128r2", SEC_OID_SECG_EC_SECP128R2},
michael@0 150 { "secp160k1", SEC_OID_SECG_EC_SECP160K1},
michael@0 151 { "secp160r1", SEC_OID_SECG_EC_SECP160R1},
michael@0 152 { "secp160r2", SEC_OID_SECG_EC_SECP160R2},
michael@0 153 { "secp192k1", SEC_OID_SECG_EC_SECP192K1},
michael@0 154 { "secp192r1", SEC_OID_ANSIX962_EC_PRIME192V1 },
michael@0 155 { "nistp192", SEC_OID_ANSIX962_EC_PRIME192V1 },
michael@0 156 { "secp224k1", SEC_OID_SECG_EC_SECP224K1},
michael@0 157 { "secp224r1", SEC_OID_SECG_EC_SECP224R1},
michael@0 158 { "nistp224", SEC_OID_SECG_EC_SECP224R1},
michael@0 159 { "secp256k1", SEC_OID_SECG_EC_SECP256K1},
michael@0 160 { "secp256r1", SEC_OID_ANSIX962_EC_PRIME256V1 },
michael@0 161 { "nistp256", SEC_OID_ANSIX962_EC_PRIME256V1 },
michael@0 162 { "secp384r1", SEC_OID_SECG_EC_SECP384R1},
michael@0 163 { "nistp384", SEC_OID_SECG_EC_SECP384R1},
michael@0 164 { "secp521r1", SEC_OID_SECG_EC_SECP521R1},
michael@0 165 { "nistp521", SEC_OID_SECG_EC_SECP521R1},
michael@0 166
michael@0 167 { "c2pnb163v1", SEC_OID_ANSIX962_EC_C2PNB163V1 },
michael@0 168 { "c2pnb163v2", SEC_OID_ANSIX962_EC_C2PNB163V2 },
michael@0 169 { "c2pnb163v3", SEC_OID_ANSIX962_EC_C2PNB163V3 },
michael@0 170 { "c2pnb176v1", SEC_OID_ANSIX962_EC_C2PNB176V1 },
michael@0 171 { "c2tnb191v1", SEC_OID_ANSIX962_EC_C2TNB191V1 },
michael@0 172 { "c2tnb191v2", SEC_OID_ANSIX962_EC_C2TNB191V2 },
michael@0 173 { "c2tnb191v3", SEC_OID_ANSIX962_EC_C2TNB191V3 },
michael@0 174 { "c2onb191v4", SEC_OID_ANSIX962_EC_C2ONB191V4 },
michael@0 175 { "c2onb191v5", SEC_OID_ANSIX962_EC_C2ONB191V5 },
michael@0 176 { "c2pnb208w1", SEC_OID_ANSIX962_EC_C2PNB208W1 },
michael@0 177 { "c2tnb239v1", SEC_OID_ANSIX962_EC_C2TNB239V1 },
michael@0 178 { "c2tnb239v2", SEC_OID_ANSIX962_EC_C2TNB239V2 },
michael@0 179 { "c2tnb239v3", SEC_OID_ANSIX962_EC_C2TNB239V3 },
michael@0 180 { "c2onb239v4", SEC_OID_ANSIX962_EC_C2ONB239V4 },
michael@0 181 { "c2onb239v5", SEC_OID_ANSIX962_EC_C2ONB239V5 },
michael@0 182 { "c2pnb272w1", SEC_OID_ANSIX962_EC_C2PNB272W1 },
michael@0 183 { "c2pnb304w1", SEC_OID_ANSIX962_EC_C2PNB304W1 },
michael@0 184 { "c2tnb359v1", SEC_OID_ANSIX962_EC_C2TNB359V1 },
michael@0 185 { "c2pnb368w1", SEC_OID_ANSIX962_EC_C2PNB368W1 },
michael@0 186 { "c2tnb431r1", SEC_OID_ANSIX962_EC_C2TNB431R1 },
michael@0 187
michael@0 188 { "sect113r1", SEC_OID_SECG_EC_SECT113R1},
michael@0 189 { "sect113r2", SEC_OID_SECG_EC_SECT113R2},
michael@0 190 { "sect131r1", SEC_OID_SECG_EC_SECT131R1},
michael@0 191 { "sect131r2", SEC_OID_SECG_EC_SECT131R2},
michael@0 192 { "sect163k1", SEC_OID_SECG_EC_SECT163K1},
michael@0 193 { "nistk163", SEC_OID_SECG_EC_SECT163K1},
michael@0 194 { "sect163r1", SEC_OID_SECG_EC_SECT163R1},
michael@0 195 { "sect163r2", SEC_OID_SECG_EC_SECT163R2},
michael@0 196 { "nistb163", SEC_OID_SECG_EC_SECT163R2},
michael@0 197 { "sect193r1", SEC_OID_SECG_EC_SECT193R1},
michael@0 198 { "sect193r2", SEC_OID_SECG_EC_SECT193R2},
michael@0 199 { "sect233k1", SEC_OID_SECG_EC_SECT233K1},
michael@0 200 { "nistk233", SEC_OID_SECG_EC_SECT233K1},
michael@0 201 { "sect233r1", SEC_OID_SECG_EC_SECT233R1},
michael@0 202 { "nistb233", SEC_OID_SECG_EC_SECT233R1},
michael@0 203 { "sect239k1", SEC_OID_SECG_EC_SECT239K1},
michael@0 204 { "sect283k1", SEC_OID_SECG_EC_SECT283K1},
michael@0 205 { "nistk283", SEC_OID_SECG_EC_SECT283K1},
michael@0 206 { "sect283r1", SEC_OID_SECG_EC_SECT283R1},
michael@0 207 { "nistb283", SEC_OID_SECG_EC_SECT283R1},
michael@0 208 { "sect409k1", SEC_OID_SECG_EC_SECT409K1},
michael@0 209 { "nistk409", SEC_OID_SECG_EC_SECT409K1},
michael@0 210 { "sect409r1", SEC_OID_SECG_EC_SECT409R1},
michael@0 211 { "nistb409", SEC_OID_SECG_EC_SECT409R1},
michael@0 212 { "sect571k1", SEC_OID_SECG_EC_SECT571K1},
michael@0 213 { "nistk571", SEC_OID_SECG_EC_SECT571K1},
michael@0 214 { "sect571r1", SEC_OID_SECG_EC_SECT571R1},
michael@0 215 { "nistb571", SEC_OID_SECG_EC_SECT571R1},
michael@0 216
michael@0 217 };
michael@0 218
michael@0 219 SECKEYECParams *
michael@0 220 decode_ec_params(const char *curve)
michael@0 221 {
michael@0 222 SECKEYECParams *ecparams;
michael@0 223 SECOidData *oidData = nullptr;
michael@0 224 SECOidTag curveOidTag = SEC_OID_UNKNOWN; /* default */
michael@0 225 int i, numCurves;
michael@0 226
michael@0 227 if (curve && *curve) {
michael@0 228 numCurves = sizeof(nameTagPair)/sizeof(CurveNameTagPair);
michael@0 229 for (i = 0; ((i < numCurves) && (curveOidTag == SEC_OID_UNKNOWN));
michael@0 230 i++) {
michael@0 231 if (PL_strcmp(curve, nameTagPair[i].curveName) == 0)
michael@0 232 curveOidTag = nameTagPair[i].curveOidTag;
michael@0 233 }
michael@0 234 }
michael@0 235
michael@0 236 /* Return nullptr if curve name is not recognized */
michael@0 237 if ((curveOidTag == SEC_OID_UNKNOWN) ||
michael@0 238 (oidData = SECOID_FindOIDByTag(curveOidTag)) == nullptr) {
michael@0 239 return nullptr;
michael@0 240 }
michael@0 241
michael@0 242 ecparams = SECITEM_AllocItem(nullptr, nullptr, (2 + oidData->oid.len));
michael@0 243
michael@0 244 if (!ecparams)
michael@0 245 return nullptr;
michael@0 246
michael@0 247 /*
michael@0 248 * ecparams->data needs to contain the ASN encoding of an object ID (OID)
michael@0 249 * representing the named curve. The actual OID is in
michael@0 250 * oidData->oid.data so we simply prepend 0x06 and OID length
michael@0 251 */
michael@0 252 ecparams->data[0] = SEC_ASN1_OBJECT_ID;
michael@0 253 ecparams->data[1] = oidData->oid.len;
michael@0 254 memcpy(ecparams->data + 2, oidData->oid.data, oidData->oid.len);
michael@0 255
michael@0 256 return ecparams;
michael@0 257 }
michael@0 258
michael@0 259 NS_IMPL_ISUPPORTS(nsKeygenFormProcessor, nsIFormProcessor)
michael@0 260
michael@0 261 nsKeygenFormProcessor::nsKeygenFormProcessor()
michael@0 262 {
michael@0 263 m_ctx = new PipUIContext();
michael@0 264
michael@0 265 }
michael@0 266
michael@0 267 nsKeygenFormProcessor::~nsKeygenFormProcessor()
michael@0 268 {
michael@0 269 }
michael@0 270
michael@0 271 nsresult
michael@0 272 nsKeygenFormProcessor::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
michael@0 273 {
michael@0 274 nsresult rv;
michael@0 275 NS_ENSURE_NO_AGGREGATION(aOuter);
michael@0 276 nsKeygenFormProcessor* formProc = new nsKeygenFormProcessor();
michael@0 277
michael@0 278 nsCOMPtr<nsISupports> stabilize = formProc;
michael@0 279 rv = formProc->Init();
michael@0 280 if (NS_SUCCEEDED(rv)) {
michael@0 281 rv = formProc->QueryInterface(aIID, aResult);
michael@0 282 }
michael@0 283 return rv;
michael@0 284 }
michael@0 285
michael@0 286 nsresult
michael@0 287 nsKeygenFormProcessor::Init()
michael@0 288 {
michael@0 289 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
michael@0 290
michael@0 291 nsresult rv;
michael@0 292
michael@0 293 nsCOMPtr<nsINSSComponent> nssComponent;
michael@0 294 nssComponent = do_GetService(kNSSComponentCID, &rv);
michael@0 295 if (NS_FAILED(rv))
michael@0 296 return rv;
michael@0 297
michael@0 298 // Init possible key size choices.
michael@0 299 nssComponent->GetPIPNSSBundleString("HighGrade", mSECKeySizeChoiceList[0].name);
michael@0 300 mSECKeySizeChoiceList[0].size = 2048;
michael@0 301
michael@0 302 nssComponent->GetPIPNSSBundleString("MediumGrade", mSECKeySizeChoiceList[1].name);
michael@0 303 mSECKeySizeChoiceList[1].size = 1024;
michael@0 304
michael@0 305 return NS_OK;
michael@0 306 }
michael@0 307
michael@0 308 nsresult
michael@0 309 nsKeygenFormProcessor::GetSlot(uint32_t aMechanism, PK11SlotInfo** aSlot)
michael@0 310 {
michael@0 311 return GetSlotWithMechanism(aMechanism,m_ctx,aSlot);
michael@0 312 }
michael@0 313
michael@0 314
michael@0 315 uint32_t MapGenMechToAlgoMech(uint32_t mechanism)
michael@0 316 {
michael@0 317 uint32_t searchMech;
michael@0 318
michael@0 319 /* We are interested in slots based on the ability to perform
michael@0 320 a given algorithm, not on their ability to generate keys usable
michael@0 321 by that algorithm. Therefore, map keygen-specific mechanism tags
michael@0 322 to tags for the corresponding crypto algorthm. */
michael@0 323 switch(mechanism)
michael@0 324 {
michael@0 325 case CKM_RSA_PKCS_KEY_PAIR_GEN:
michael@0 326 searchMech = CKM_RSA_PKCS;
michael@0 327 break;
michael@0 328 case CKM_DSA_KEY_PAIR_GEN:
michael@0 329 searchMech = CKM_DSA;
michael@0 330 break;
michael@0 331 case CKM_RC4_KEY_GEN:
michael@0 332 searchMech = CKM_RC4;
michael@0 333 break;
michael@0 334 case CKM_DH_PKCS_KEY_PAIR_GEN:
michael@0 335 searchMech = CKM_DH_PKCS_DERIVE; /* ### mwelch is this right? */
michael@0 336 break;
michael@0 337 case CKM_DES_KEY_GEN:
michael@0 338 /* What do we do about DES keygen? Right now, we're just using
michael@0 339 DES_KEY_GEN to look for tokens, because otherwise we'll have
michael@0 340 to search the token list three times. */
michael@0 341 case CKM_EC_KEY_PAIR_GEN:
michael@0 342 /* The default should also work for EC key pair generation. */
michael@0 343 default:
michael@0 344 searchMech = mechanism;
michael@0 345 break;
michael@0 346 }
michael@0 347 return searchMech;
michael@0 348 }
michael@0 349
michael@0 350
michael@0 351 nsresult
michael@0 352 GetSlotWithMechanism(uint32_t aMechanism,
michael@0 353 nsIInterfaceRequestor *m_ctx,
michael@0 354 PK11SlotInfo** aSlot)
michael@0 355 {
michael@0 356 nsNSSShutDownPreventionLock locker;
michael@0 357 PK11SlotList * slotList = nullptr;
michael@0 358 char16_t** tokenNameList = nullptr;
michael@0 359 nsITokenDialogs * dialogs;
michael@0 360 char16_t *unicodeTokenChosen;
michael@0 361 PK11SlotListElement *slotElement, *tmpSlot;
michael@0 362 uint32_t numSlots = 0, i = 0;
michael@0 363 bool canceled;
michael@0 364 nsresult rv = NS_OK;
michael@0 365
michael@0 366 *aSlot = nullptr;
michael@0 367
michael@0 368 // Get the slot
michael@0 369 slotList = PK11_GetAllTokens(MapGenMechToAlgoMech(aMechanism),
michael@0 370 true, true, m_ctx);
michael@0 371 if (!slotList || !slotList->head) {
michael@0 372 rv = NS_ERROR_FAILURE;
michael@0 373 goto loser;
michael@0 374 }
michael@0 375
michael@0 376 if (!slotList->head->next) {
michael@0 377 /* only one slot available, just return it */
michael@0 378 *aSlot = slotList->head->slot;
michael@0 379 } else {
michael@0 380 // Gerenate a list of slots and ask the user to choose //
michael@0 381 tmpSlot = slotList->head;
michael@0 382 while (tmpSlot) {
michael@0 383 numSlots++;
michael@0 384 tmpSlot = tmpSlot->next;
michael@0 385 }
michael@0 386
michael@0 387 // Allocate the slot name buffer //
michael@0 388 tokenNameList = static_cast<char16_t**>(nsMemory::Alloc(sizeof(char16_t *) * numSlots));
michael@0 389 if (!tokenNameList) {
michael@0 390 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 391 goto loser;
michael@0 392 }
michael@0 393
michael@0 394 i = 0;
michael@0 395 slotElement = PK11_GetFirstSafe(slotList);
michael@0 396 while (slotElement) {
michael@0 397 tokenNameList[i] = UTF8ToNewUnicode(nsDependentCString(PK11_GetTokenName(slotElement->slot)));
michael@0 398 slotElement = PK11_GetNextSafe(slotList, slotElement, false);
michael@0 399 if (tokenNameList[i])
michael@0 400 i++;
michael@0 401 else {
michael@0 402 // OOM. adjust numSlots so we don't free unallocated memory.
michael@0 403 numSlots = i;
michael@0 404 PK11_FreeSlotListElement(slotList, slotElement);
michael@0 405 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 406 goto loser;
michael@0 407 }
michael@0 408 }
michael@0 409
michael@0 410 /* Throw up the token list dialog and get back the token */
michael@0 411 rv = getNSSDialogs((void**)&dialogs,
michael@0 412 NS_GET_IID(nsITokenDialogs),
michael@0 413 NS_TOKENDIALOGS_CONTRACTID);
michael@0 414
michael@0 415 if (NS_FAILED(rv)) goto loser;
michael@0 416
michael@0 417 {
michael@0 418 nsPSMUITracker tracker;
michael@0 419 if (!tokenNameList || !*tokenNameList) {
michael@0 420 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 421 }
michael@0 422 else if (tracker.isUIForbidden()) {
michael@0 423 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 424 }
michael@0 425 else {
michael@0 426 rv = dialogs->ChooseToken(m_ctx, (const char16_t**)tokenNameList, numSlots, &unicodeTokenChosen, &canceled);
michael@0 427 }
michael@0 428 }
michael@0 429 NS_RELEASE(dialogs);
michael@0 430 if (NS_FAILED(rv)) goto loser;
michael@0 431
michael@0 432 if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
michael@0 433
michael@0 434 // Get the slot //
michael@0 435 slotElement = PK11_GetFirstSafe(slotList);
michael@0 436 nsAutoString tokenStr(unicodeTokenChosen);
michael@0 437 while (slotElement) {
michael@0 438 if (tokenStr.Equals(NS_ConvertUTF8toUTF16(PK11_GetTokenName(slotElement->slot)))) {
michael@0 439 *aSlot = slotElement->slot;
michael@0 440 PK11_FreeSlotListElement(slotList, slotElement);
michael@0 441 break;
michael@0 442 }
michael@0 443 slotElement = PK11_GetNextSafe(slotList, slotElement, false);
michael@0 444 }
michael@0 445 if(!(*aSlot)) {
michael@0 446 rv = NS_ERROR_FAILURE;
michael@0 447 goto loser;
michael@0 448 }
michael@0 449 }
michael@0 450
michael@0 451 // Get a reference to the slot //
michael@0 452 PK11_ReferenceSlot(*aSlot);
michael@0 453 loser:
michael@0 454 if (slotList) {
michael@0 455 PK11_FreeSlotList(slotList);
michael@0 456 }
michael@0 457 if (tokenNameList) {
michael@0 458 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numSlots, tokenNameList);
michael@0 459 }
michael@0 460 return rv;
michael@0 461 }
michael@0 462
michael@0 463 nsresult
michael@0 464 nsKeygenFormProcessor::GetPublicKey(nsAString& aValue, nsAString& aChallenge,
michael@0 465 nsAFlatString& aKeyType,
michael@0 466 nsAString& aOutPublicKey, nsAString& aKeyParams)
michael@0 467 {
michael@0 468 nsNSSShutDownPreventionLock locker;
michael@0 469 nsresult rv = NS_ERROR_FAILURE;
michael@0 470 char *keystring = nullptr;
michael@0 471 char *keyparamsString = nullptr, *str = nullptr;
michael@0 472 uint32_t keyGenMechanism;
michael@0 473 int32_t primeBits;
michael@0 474 PK11SlotInfo *slot = nullptr;
michael@0 475 PK11RSAGenParams rsaParams;
michael@0 476 SECOidTag algTag;
michael@0 477 int keysize = 0;
michael@0 478 void *params;
michael@0 479 SECKEYPrivateKey *privateKey = nullptr;
michael@0 480 SECKEYPublicKey *publicKey = nullptr;
michael@0 481 CERTSubjectPublicKeyInfo *spkInfo = nullptr;
michael@0 482 PLArenaPool *arena = nullptr;
michael@0 483 SECStatus sec_rv = SECFailure;
michael@0 484 SECItem spkiItem;
michael@0 485 SECItem pkacItem;
michael@0 486 SECItem signedItem;
michael@0 487 CERTPublicKeyAndChallenge pkac;
michael@0 488 pkac.challenge.data = nullptr;
michael@0 489 nsIGeneratingKeypairInfoDialogs * dialogs;
michael@0 490 nsKeygenThread *KeygenRunnable = 0;
michael@0 491 nsCOMPtr<nsIKeygenThread> runnable;
michael@0 492
michael@0 493 // permanent and sensitive flags for keygen
michael@0 494 PK11AttrFlags attrFlags = PK11_ATTR_TOKEN | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE;
michael@0 495
michael@0 496 // Get the key size //
michael@0 497 for (size_t i = 0; i < number_of_key_size_choices; ++i) {
michael@0 498 if (aValue.Equals(mSECKeySizeChoiceList[i].name)) {
michael@0 499 keysize = mSECKeySizeChoiceList[i].size;
michael@0 500 break;
michael@0 501 }
michael@0 502 }
michael@0 503 if (!keysize) {
michael@0 504 goto loser;
michael@0 505 }
michael@0 506
michael@0 507 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 508 if (!arena) {
michael@0 509 goto loser;
michael@0 510 }
michael@0 511
michael@0 512 // Set the keygen mechanism
michael@0 513 if (aKeyType.IsEmpty() || aKeyType.LowerCaseEqualsLiteral("rsa")) {
michael@0 514 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
michael@0 515 } else if (aKeyType.LowerCaseEqualsLiteral("dsa")) {
michael@0 516 char * end;
michael@0 517 keyparamsString = ToNewCString(aKeyParams);
michael@0 518 if (!keyparamsString) {
michael@0 519 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 520 goto loser;
michael@0 521 }
michael@0 522
michael@0 523 keyGenMechanism = CKM_DSA_KEY_PAIR_GEN;
michael@0 524 if (strcmp(keyparamsString, "null") == 0)
michael@0 525 goto loser;
michael@0 526 str = keyparamsString;
michael@0 527 bool found_match = false;
michael@0 528 do {
michael@0 529 end = strchr(str, ',');
michael@0 530 if (end)
michael@0 531 *end = '\0';
michael@0 532 primeBits = pqg_prime_bits(str);
michael@0 533 if (keysize == primeBits) {
michael@0 534 found_match = true;
michael@0 535 break;
michael@0 536 }
michael@0 537 str = end + 1;
michael@0 538 } while (end);
michael@0 539 if (!found_match) {
michael@0 540 goto loser;
michael@0 541 }
michael@0 542 } else if (aKeyType.LowerCaseEqualsLiteral("ec")) {
michael@0 543 keyparamsString = ToNewCString(aKeyParams);
michael@0 544 if (!keyparamsString) {
michael@0 545 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 546 goto loser;
michael@0 547 }
michael@0 548
michael@0 549 keyGenMechanism = CKM_EC_KEY_PAIR_GEN;
michael@0 550 /* ecParams are initialized later */
michael@0 551 } else {
michael@0 552 goto loser;
michael@0 553 }
michael@0 554
michael@0 555 // Get the slot
michael@0 556 rv = GetSlot(keyGenMechanism, &slot);
michael@0 557 if (NS_FAILED(rv)) {
michael@0 558 goto loser;
michael@0 559 }
michael@0 560 switch (keyGenMechanism) {
michael@0 561 case CKM_RSA_PKCS_KEY_PAIR_GEN:
michael@0 562 rsaParams.keySizeInBits = keysize;
michael@0 563 rsaParams.pe = DEFAULT_RSA_KEYGEN_PE;
michael@0 564 algTag = DEFAULT_RSA_KEYGEN_ALG;
michael@0 565 params = &rsaParams;
michael@0 566 break;
michael@0 567 case CKM_DSA_KEY_PAIR_GEN:
michael@0 568 // XXX Fix this! XXX //
michael@0 569 goto loser;
michael@0 570 case CKM_EC_KEY_PAIR_GEN:
michael@0 571 /* XXX We ought to rethink how the KEYGEN tag is
michael@0 572 * displayed. The pulldown selections presented
michael@0 573 * to the user must depend on the keytype.
michael@0 574 * The displayed selection could be picked
michael@0 575 * from the keyparams attribute (this is currently called
michael@0 576 * the pqg attribute).
michael@0 577 * For now, we pick ecparams from the keyparams field
michael@0 578 * if it specifies a valid supported curve, or else
michael@0 579 * we pick one of secp384r1, secp256r1 or secp192r1
michael@0 580 * respectively depending on the user's selection
michael@0 581 * (High, Medium, Low).
michael@0 582 * (RSA uses RSA-2048, RSA-1024 and RSA-512 for historical
michael@0 583 * reasons, while ECC choices represent a stronger mapping)
michael@0 584 * NOTE: The user's selection
michael@0 585 * is silently ignored when a valid curve is presented
michael@0 586 * in keyparams.
michael@0 587 */
michael@0 588 if ((params = decode_ec_params(keyparamsString)) == nullptr) {
michael@0 589 /* The keyparams attribute did not specify a valid
michael@0 590 * curve name so use a curve based on the keysize.
michael@0 591 * NOTE: Here keysize is used only as an indication of
michael@0 592 * High/Medium/Low strength; elliptic curve
michael@0 593 * cryptography uses smaller keys than RSA to provide
michael@0 594 * equivalent security.
michael@0 595 */
michael@0 596 switch (keysize) {
michael@0 597 case 2048:
michael@0 598 params = decode_ec_params("secp384r1");
michael@0 599 break;
michael@0 600 case 1024:
michael@0 601 case 512:
michael@0 602 params = decode_ec_params("secp256r1");
michael@0 603 break;
michael@0 604 }
michael@0 605 }
michael@0 606 /* XXX The signature algorithm ought to choose the hashing
michael@0 607 * algorithm based on key size once ECDSA variations based
michael@0 608 * on SHA256 SHA384 and SHA512 are standardized.
michael@0 609 */
michael@0 610 algTag = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST;
michael@0 611 break;
michael@0 612 default:
michael@0 613 goto loser;
michael@0 614 }
michael@0 615
michael@0 616 /* Make sure token is initialized. */
michael@0 617 rv = setPassword(slot, m_ctx);
michael@0 618 if (NS_FAILED(rv))
michael@0 619 goto loser;
michael@0 620
michael@0 621 sec_rv = PK11_Authenticate(slot, true, m_ctx);
michael@0 622 if (sec_rv != SECSuccess) {
michael@0 623 goto loser;
michael@0 624 }
michael@0 625
michael@0 626 rv = getNSSDialogs((void**)&dialogs,
michael@0 627 NS_GET_IID(nsIGeneratingKeypairInfoDialogs),
michael@0 628 NS_GENERATINGKEYPAIRINFODIALOGS_CONTRACTID);
michael@0 629
michael@0 630 if (NS_SUCCEEDED(rv)) {
michael@0 631 KeygenRunnable = new nsKeygenThread();
michael@0 632 NS_IF_ADDREF(KeygenRunnable);
michael@0 633 }
michael@0 634
michael@0 635 if (NS_FAILED(rv) || !KeygenRunnable) {
michael@0 636 rv = NS_OK;
michael@0 637 privateKey = PK11_GenerateKeyPairWithFlags(slot, keyGenMechanism, params,
michael@0 638 &publicKey, attrFlags, m_ctx);
michael@0 639 } else {
michael@0 640 KeygenRunnable->SetParams( slot, attrFlags, nullptr, 0,
michael@0 641 keyGenMechanism, params, m_ctx );
michael@0 642
michael@0 643 runnable = do_QueryInterface(KeygenRunnable);
michael@0 644
michael@0 645 if (runnable) {
michael@0 646 {
michael@0 647 nsPSMUITracker tracker;
michael@0 648 if (tracker.isUIForbidden()) {
michael@0 649 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 650 }
michael@0 651 else {
michael@0 652 rv = dialogs->DisplayGeneratingKeypairInfo(m_ctx, runnable);
michael@0 653 // We call join on the thread,
michael@0 654 // so we can be sure that no simultaneous access to the passed parameters will happen.
michael@0 655 KeygenRunnable->Join();
michael@0 656 }
michael@0 657 }
michael@0 658
michael@0 659 NS_RELEASE(dialogs);
michael@0 660 if (NS_SUCCEEDED(rv)) {
michael@0 661 PK11SlotInfo *used_slot = nullptr;
michael@0 662 rv = KeygenRunnable->ConsumeResult(&used_slot, &privateKey, &publicKey);
michael@0 663 if (NS_SUCCEEDED(rv) && used_slot) {
michael@0 664 PK11_FreeSlot(used_slot);
michael@0 665 }
michael@0 666 }
michael@0 667 }
michael@0 668 }
michael@0 669
michael@0 670 if (NS_FAILED(rv) || !privateKey) {
michael@0 671 goto loser;
michael@0 672 }
michael@0 673 // just in case we'll need to authenticate to the db -jp //
michael@0 674 privateKey->wincx = m_ctx;
michael@0 675
michael@0 676 /*
michael@0 677 * Create a subject public key info from the public key.
michael@0 678 */
michael@0 679 spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
michael@0 680 if ( !spkInfo ) {
michael@0 681 goto loser;
michael@0 682 }
michael@0 683
michael@0 684 /*
michael@0 685 * Now DER encode the whole subjectPublicKeyInfo.
michael@0 686 */
michael@0 687 sec_rv=DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate, spkInfo);
michael@0 688 if (sec_rv != SECSuccess) {
michael@0 689 goto loser;
michael@0 690 }
michael@0 691
michael@0 692 /*
michael@0 693 * set up the PublicKeyAndChallenge data structure, then DER encode it
michael@0 694 */
michael@0 695 pkac.spki = spkiItem;
michael@0 696 pkac.challenge.len = aChallenge.Length();
michael@0 697 pkac.challenge.data = (unsigned char *)ToNewCString(aChallenge);
michael@0 698 if (!pkac.challenge.data) {
michael@0 699 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 700 goto loser;
michael@0 701 }
michael@0 702
michael@0 703 sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate, &pkac);
michael@0 704 if ( sec_rv != SECSuccess ) {
michael@0 705 goto loser;
michael@0 706 }
michael@0 707
michael@0 708 /*
michael@0 709 * now sign the DER encoded PublicKeyAndChallenge
michael@0 710 */
michael@0 711 sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len,
michael@0 712 privateKey, algTag);
michael@0 713 if ( sec_rv != SECSuccess ) {
michael@0 714 goto loser;
michael@0 715 }
michael@0 716
michael@0 717 /*
michael@0 718 * Convert the signed public key and challenge into base64/ascii.
michael@0 719 */
michael@0 720 keystring = BTOA_DataToAscii(signedItem.data, signedItem.len);
michael@0 721 if (!keystring) {
michael@0 722 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 723 goto loser;
michael@0 724 }
michael@0 725
michael@0 726 CopyASCIItoUTF16(keystring, aOutPublicKey);
michael@0 727 free(keystring);
michael@0 728
michael@0 729 rv = NS_OK;
michael@0 730 loser:
michael@0 731 if ( sec_rv != SECSuccess ) {
michael@0 732 if ( privateKey ) {
michael@0 733 PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
michael@0 734 }
michael@0 735 if ( publicKey ) {
michael@0 736 PK11_DestroyTokenObject(publicKey->pkcs11Slot,publicKey->pkcs11ID);
michael@0 737 }
michael@0 738 }
michael@0 739 if ( spkInfo ) {
michael@0 740 SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
michael@0 741 }
michael@0 742 if ( publicKey ) {
michael@0 743 SECKEY_DestroyPublicKey(publicKey);
michael@0 744 }
michael@0 745 if ( privateKey ) {
michael@0 746 SECKEY_DestroyPrivateKey(privateKey);
michael@0 747 }
michael@0 748 if ( arena ) {
michael@0 749 PORT_FreeArena(arena, true);
michael@0 750 }
michael@0 751 if (slot) {
michael@0 752 PK11_FreeSlot(slot);
michael@0 753 }
michael@0 754 if (KeygenRunnable) {
michael@0 755 NS_RELEASE(KeygenRunnable);
michael@0 756 }
michael@0 757 if (keyparamsString) {
michael@0 758 nsMemory::Free(keyparamsString);
michael@0 759 }
michael@0 760 if (pkac.challenge.data) {
michael@0 761 nsMemory::Free(pkac.challenge.data);
michael@0 762 }
michael@0 763 return rv;
michael@0 764 }
michael@0 765
michael@0 766 NS_METHOD
michael@0 767 nsKeygenFormProcessor::ProcessValue(nsIDOMHTMLElement *aElement,
michael@0 768 const nsAString& aName,
michael@0 769 nsAString& aValue)
michael@0 770 {
michael@0 771 nsAutoString challengeValue;
michael@0 772 nsAutoString keyTypeValue;
michael@0 773 nsAutoString keyParamsValue;
michael@0 774
michael@0 775 aElement->GetAttribute(NS_LITERAL_STRING("keytype"), keyTypeValue);
michael@0 776 if (keyTypeValue.IsEmpty()) {
michael@0 777 // If this field is not present, we default to rsa.
michael@0 778 keyTypeValue.AssignLiteral("rsa");
michael@0 779 }
michael@0 780
michael@0 781 aElement->GetAttribute(NS_LITERAL_STRING("pqg"),
michael@0 782 keyParamsValue);
michael@0 783 /* XXX We can still support the pqg attribute in the keygen
michael@0 784 * tag for backward compatibility while introducing a more
michael@0 785 * general attribute named keyparams.
michael@0 786 */
michael@0 787 if (keyParamsValue.IsEmpty()) {
michael@0 788 aElement->GetAttribute(NS_LITERAL_STRING("keyparams"),
michael@0 789 keyParamsValue);
michael@0 790 }
michael@0 791
michael@0 792 aElement->GetAttribute(NS_LITERAL_STRING("challenge"), challengeValue);
michael@0 793
michael@0 794 return GetPublicKey(aValue, challengeValue, keyTypeValue,
michael@0 795 aValue, keyParamsValue);
michael@0 796 }
michael@0 797
michael@0 798 NS_METHOD nsKeygenFormProcessor::ProvideContent(const nsAString& aFormType,
michael@0 799 nsTArray<nsString>& aContent,
michael@0 800 nsAString& aAttribute)
michael@0 801 {
michael@0 802 if (Compare(aFormType, NS_LITERAL_STRING("SELECT"),
michael@0 803 nsCaseInsensitiveStringComparator()) == 0) {
michael@0 804
michael@0 805 for (size_t i = 0; i < number_of_key_size_choices; ++i) {
michael@0 806 aContent.AppendElement(mSECKeySizeChoiceList[i].name);
michael@0 807 }
michael@0 808 aAttribute.AssignLiteral("-mozilla-keygen");
michael@0 809 }
michael@0 810 return NS_OK;
michael@0 811 }
michael@0 812

mercurial