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