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