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