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