bug 964: Reduce variable scope where possible
[cacert-devel.git] / www / keygenIE.js
1 /*
2 LibreSSL - CAcert web application
3 Copyright (C) 2004-2012 CAcert Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 var CAcert_keygen_IE = function () {
20
21 /// Makes a new DOM text node
22 var textnode = function (text) {
23 return document.createTextNode(text);
24 }
25
26 /// makes a new <p> element
27 var paragraph = function (text) {
28 var paragraph = document.createElement("p");
29 paragraph.appendChild(textnode(text));
30 return paragraph;
31 }
32
33 /// makes a new <pre> elemtent
34 var pre = function (text) {
35 var pre = document.createElement("pre");
36 pre.appendChild(textnode(text));
37 return pre;
38 }
39
40 /// makes a new <option> element
41 var option = function (text, value) {
42 var option = document.createElement("option");
43 if (value !== undefined) {
44 option.setAttribute("value", value);
45 }
46 option.appendChild(textnode(text));
47 return option;
48 }
49
50 /// Removes all child nodes from the element
51 var removeChildren = function (element) {
52 element.innerHTML = "";
53 }
54
55 /// Show error message to user from exception
56 var showError = function (message, exception) {
57 window.alert(
58 message +
59 "\n\nError: " + exception.message +
60 " (0x" + (0xFFFFFFFF + exception.number + 1).toString(16) +
61 " / " + exception.number + ")"
62 );
63 }
64
65 // Get important elements from the DOM
66 var form = document.getElementById("CertReqForm");
67 var securityLevel = document.getElementById("SecurityLevel");
68 var customSettings = document.getElementById("customSettings");
69 var provider = document.getElementById("CspProvider");
70 var algorithm = document.getElementById("algorithm");
71 var algorithmParagraph = document.getElementById("algorithmParagraph");
72 var keySize = document.getElementById("keySize");
73 var keySizeMin = document.getElementById("keySizeMin");
74 var keySizeMax = document.getElementById("keySizeMax");
75 var keySizeStep = document.getElementById("keySizeStep");
76 var genReq = document.getElementById("GenReq");
77 var csr = document.getElementById("CSR");
78 var noActiveX = document.getElementById("noActiveX");
79 var generatingKeyNotice = document.getElementById("generatingKeyNotice");
80 var createRequestErrorChooseAlgorithm = document.getElementById("createRequestErrorChooseAlgorithm");
81 var createRequestErrorConfirmDialogue = document.getElementById("createRequestErrorConfirmDialogue");
82 var createRequestErrorConnectDevice = document.getElementById("createRequestErrorConnectDevice");
83 var createRequestError = document.getElementById("createRequestError");
84 var invalidKeySizeError = document.getElementById("invalidKeySizeError");
85 var unsupportedPlatformError = document.getElementById("unsupportedPlatformError");
86
87 /// Initialise the CertEnroll code (Vista and higher)
88 /// returns false if initialisation fails
89 var initCertEnroll = function () {
90 var factory = null;
91 var providerList = null;
92 var cspStats = null;
93
94 // Try to initialise the ActiveX element. Requires permissions by the user
95 try {
96 factory = new ActiveXObject("X509Enrollment.CX509EnrollmentWebClassFactory");
97 if (!factory) {
98 throw {
99 name: "NoObjectError",
100 message: "Got null at object creation"
101 };
102 }
103
104 // also try to create a useless object here so the library gets
105 // initialised and we don't need to check everytime later
106 factory.CreateObject("X509Enrollment.CObjectId");
107
108 form.style.display = "";
109 noActiveX.style.display = "none";
110 } catch (e) {
111 return false;
112 }
113
114 /// Get the selected provider
115 var getProvider = function () {
116 var providerIndex = provider.options[provider.selectedIndex].value;
117 return providerList.ItemByIndex(providerIndex);
118 }
119
120 /// Get the selected algorithm
121 var getAlgorithm = function () {
122 var algorithmIndex = algorithm.options[algorithm.selectedIndex].value;
123 return alg = cspStats.ItemByIndex(algorithmIndex).CspAlgorithm;
124 }
125
126 /// Get the selected key size
127 var getKeySize = function () {
128 var alg = getAlgorithm();
129
130 var bits = parseInt(keySize.value, 10);
131 if ( bits < alg.MinLength || bits > alg.MaxLength ||
132 (alg.IncrementLength &&
133 (bits - alg.MinLength) % alg.IncrementLength !== 0)
134 )
135 {
136 return false;
137 }
138
139 return bits;
140 }
141
142 /// Fill the key size list
143 var getKeySizeList = function () {
144 if (!cspStats) {
145 return false;
146 }
147
148 var alg = getAlgorithm();
149
150 // HTML5 attributes
151 keySize.setAttribute("min", alg.MinLength);
152 keySize.setAttribute("max", alg.MaxLength);
153 keySize.setAttribute("step", alg.IncrementLength);
154 keySize.setAttribute("value", alg.DefaultLength);
155 keySize.value = ""+alg.DefaultLength;
156
157 // ugly, but buggy otherwise if done with text nodes
158 keySizeMin.innerHTML = alg.MinLength;
159 keySizeMax.innerHTML = alg.MaxLength;
160 keySizeStep.innerHTML = alg.IncrementLength;
161
162 return true;
163 }
164
165 /// Fill the algorithm list
166 var getAlgorithmList = function () {
167 if (!providerList) {
168 return false;
169 }
170
171 var csp = getProvider();
172
173 cspStats = providerList.GetCspStatusesFromOperations(
174 0x1c, //XCN_NCRYPT_ANY_ASYMMETRIC_OPERATION
175 //0x10, //XCN_NCRYPT_SIGNATURE_OPERATION
176 //0x8, //XCN_NCRYPT_SECRET_AGREEMENT_OPERATION
177 //0x4, //XCN_NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION
178 csp
179 );
180
181 removeChildren(algorithm);
182 for (var i = 0; i < cspStats.Count; i++) {
183 var alg = cspStats.ItemByIndex(i).CspAlgorithm;
184 algorithm.appendChild(option(alg.Name, i));
185 }
186
187 return getKeySizeList();
188 }
189
190 /// Fill the crypto provider list
191 var getProviderList = function () {
192 var csps = factory.CreateObject("X509Enrollment.CCspInformations");
193
194 // Get provider information
195 csps.AddAvailableCsps();
196
197 removeChildren(provider);
198
199 for (var i = 0; i < csps.Count; i++) {
200 var csp = csps.ItemByIndex(i);
201 provider.appendChild(option(csp.Name, i));
202 }
203
204 providerList = csps;
205
206 return getAlgorithmList();
207 }
208
209 /// Generate a key and create and submit the actual CSR
210 var createCSR = function () {
211 var providerName, algorithmOid, bits;
212
213 var level = securityLevel.options[securityLevel.selectedIndex];
214 if (level.value === "custom") {
215 providerName = getProvider().Name;
216 var alg = getAlgorithm();
217 algorithmOid = alg.GetAlgorithmOid(0, 0)
218 bits = getKeySize();
219 if (!bits) {
220 window.alert(invalidKeySizeError.innerHTML);
221 return false;
222 }
223 } else {
224 providerName = "Microsoft Software Key Storage Provider";
225
226 algorithmOid = factory.CreateObject("X509Enrollment.CObjectId");
227 algorithmOid.InitializeFromValue("1.2.840.113549.1.1.1"); // RSA
228 // "1.2.840.10040.4.1" == DSA
229 // "1.2.840.10046.2.1" == DH
230
231 if (level.value === "high") {
232 bits = 4096;
233 } else { // medium
234 bits = 2048;
235 }
236 }
237
238 var privateKey = factory.CreateObject("X509Enrollment.CX509PrivateKey");
239 privateKey.ProviderName = providerName;
240 privateKey.Algorithm = algorithmOid;
241 privateKey.Length = bits;
242 privateKey.KeyUsage = 0xffffff; // XCN_NCRYPT_ALLOW_ALL_USAGES
243
244 var request = factory.CreateObject("X509Enrollment.CX509CertificateRequestPkcs10");
245 request.InitializeFromPrivateKey(
246 1, // ContextUser
247 privateKey,
248 "" // don't use a template
249 );
250
251 var enroll = factory.CreateObject("X509Enrollment.CX509Enrollment");
252 enroll.InitializeFromRequest(request);
253
254 generatingKeyNotice.style.display = "";
255
256 // The request needs to be created after we return so the "please wait"
257 // message gets rendered
258 var createCSRHandler = function () {
259 try {
260 csr.value = enroll.CreateRequest(0x1); //XCN_CRYPT_STRING_BASE64
261 form.submit();
262 } catch (e) {
263 showError(createRequestErrorChooseAlgorithm.innerHTML, e);
264 }
265
266 generatingKeyNotice.style.display = "none";
267 }
268
269 window.setTimeout(createCSRHandler, 0);
270
271 // Always return false, form is submitted by deferred method
272 return false;
273 }
274
275 /// Call if securityLevel has changed
276 var refreshSecurityLevel = function () {
277 var level = securityLevel.options[securityLevel.selectedIndex];
278 if (level.value === "custom") {
279 getProviderList();
280 customSettings.style.display = "";
281 } else {
282 customSettings.style.display = "none";
283 }
284 }
285
286 securityLevel.onchange = refreshSecurityLevel;
287 provider.onchange = getAlgorithmList;
288 algorithm.onchange = getKeySizeList;
289 genReq.onclick = createCSR;
290
291 return true;
292 } // end of initCertEnroll()
293
294 /// Initialise Xenroll code (XP and lower)
295 /// returns false if initialisation fails
296 var initXEnroll = function () {
297 cenroll = null;
298
299 providerTypes = Array(
300 1, //PROV_RSA_FULL
301 2, //PROV_RSA_SIG
302 3, //PROV_DSS
303 4, //PROV_FORTEZZA
304 5, //PROV_MS_EXCHANGE
305 6, //PROV_SSL
306 12, //PROV_RSA_SCHANNEL
307 13, //PROV_DSS_DH
308 14, //PROV_EC_ECDSA_SIG
309 15, //PROV_EC_ECNRA_SIG
310 16, //PROV_EC_ECDSA_FULL
311 17, //PROV_EC_ECNRA_FULL
312 18, //PROV_DH_SCHANNEL
313 20, //PROV_SPYRUS_LYNKS
314 21, //PROV_RNG
315 22, //PROV_INTEL_SEC
316 23, //PROV_REPLACE_OWF
317 24 //PROV_RSA_AES
318 );
319
320 algClasses = Array(
321 1 << 13, //ALG_CLASS_SIGNATURE
322 //2 << 13, //ALG_CLASS_MSG_ENCRYPT
323 //3 << 13, //ALG_CLASS_DATA_ENCRYPT
324 //4 << 13, //ALG_CLASS_HASH
325 5 << 13 //ALG_CLASS_KEY_EXCHANGE
326 );
327
328 // Try to initialise the ActiveX element.
329 try {
330 cenroll = new ActiveXObject("CEnroll.CEnroll");
331
332 if (!cenroll) {
333 throw {
334 name: "NoObjectError",
335 message: "Got null at object creation"
336 };
337 }
338
339 form.style.display = "";
340 algorithm.disabled = true;
341 noActiveX.style.display = "none";
342 } catch (e) {
343 return false;
344 }
345
346 /// Get the name of the selected provider
347 var getProviderName = function () {
348 return provider.options[provider.selectedIndex].text;
349 }
350
351 /// Get the type of the selected provider
352 var getProviderType = function () {
353 return parseInt(provider.options[provider.selectedIndex].value, 10);
354 }
355
356 var refreshProvider = function () {
357 cenroll.ProviderName = getProviderName();
358 cenroll.ProviderType = getProviderType();
359 }
360
361 /// Get the ID of the selected algorithm
362 var getAlgorithmId = function () {
363 return parseInt(algorithm.options[algorithm.selectedIndex].value, 10);
364 }
365
366 /// Minimum bit length for exchange keys
367 var getMinExKeyLength = function () {
368 refreshProvider();
369
370 try {
371 return cenroll.GetKeyLen(true, true);
372 } catch (e) {
373 return false;
374 }
375 }
376
377 /// Maximum bit length for exchange keys
378 var getMaxExKeyLength = function () {
379 refreshProvider();
380
381 try {
382 return cenroll.GetKeyLen(false, true);
383 } catch (e) {
384 return false;
385 }
386 }
387
388 /// Step size for exchange keys
389 /// This might not be available on older platforms
390 var getStepExKeyLength = function () {
391 refreshProvider();
392
393 try {
394 return cenroll.GetKeyLenEx(3, 1);
395 } catch (e) {
396 return false;
397 }
398 }
399
400 /// Minimum bit length for signature keys
401 var getMinSigKeyLength = function () {
402 refreshProvider();
403
404 try {
405 return cenroll.GetKeyLen(true, false);
406 } catch (e) {
407 return false;
408 }
409 }
410
411 /// Maximum bit length for signature keys
412 var getMaxSigKeyLength = function () {
413 refreshProvider();
414
415 try {
416 return cenroll.GetKeyLen(false, false);
417 } catch (e) {
418 return false;
419 }
420 }
421
422 /// Step size for signature keys
423 /// This might not be available on older platforms
424 var getStepSigKeyLength = function () {
425 refreshProvider();
426
427 try {
428 return cenroll.GetKeyLenEx(3, 2);
429 } catch (e) {
430 return false;
431 }
432 }
433
434 /// Get the selected key size
435 var getKeySize = function () {
436 var bits = parseInt(keySize.value, 10);
437 if ( bits < getMinSigKeyLength() || bits > getMaxSigKeyLength() ||
438 (getStepSigKeyLength() &&
439 (bits - getMinSigKeyLength()) %
440 getStepSigKeyLength() !== 0)
441 )
442 {
443 return false;
444 }
445
446 return bits;
447 }
448
449 var getKeySizeLimits = function () {
450 // HTML5 attributes
451 keySize.setAttribute("min", getMinSigKeyLength());
452 keySize.setAttribute("max", getMaxSigKeyLength());
453 if (getStepSigKeyLength()) {
454 keySize.setAttribute("step", getStepSigKeyLength());
455 }
456
457 // ugly, but buggy otherwise if done with text nodes
458 keySizeMin.innerHTML = getMinSigKeyLength();
459 keySizeMax.innerHTML = getMaxSigKeyLength();
460 keySizeStep.innerHTML = getStepSigKeyLength();
461
462 if (getMinSigKeyLength() === getMaxSigKeyLength()) {
463 keySize.value = getMaxSigKeyLength();
464 }
465
466 return true;
467 }
468
469 /// Fill the algorithm selection box
470 var getAlgorithmList = function () {
471 refreshProvider();
472
473 removeChildren(algorithm);
474
475 for (var i = 0; i < algClasses.length; ++i) {
476 for (var j = 0; true; ++j) {
477 try {
478 var algId = cenroll.EnumAlgs(j, algClasses[i]);
479 var algName = cenroll.GetAlgName(algId);
480 algorithm.appendChild(option(algName, algId));
481 } catch (e) {
482 break;
483 }
484 }
485 }
486
487 getKeySizeLimits();
488 }
489
490 /// Fill the provider selection box
491 var getProviderList = function () {
492 removeChildren(provider);
493
494 for (var i = 0; i < providerTypes.length; ++i) {
495 cenroll.providerType = providerTypes[i];
496
497 var providerName = "invalid";
498 for (var j = 0; true; ++j) {
499 try {
500 providerName = cenroll.enumProviders(j, 0);
501 provider.appendChild(option(providerName, providerTypes[i]));
502 } catch (e) {
503 break;
504 }
505 }
506 }
507
508 return getAlgorithmList();
509 }
510
511 var createCSR = function () {
512 var providerName, bits;
513
514 var level = securityLevel.options[securityLevel.selectedIndex];
515 if (level.value === "custom") {
516 refreshProvider();
517
518 bits = getKeySize();
519 if (bits === false) {
520 window.alert(invalidKeySizeError.innerHTML);
521 return false;
522 }
523 } else {
524 cenroll.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
525 cenroll.ProviderType = 1; //PROV_RSA_FULL
526
527 if (level.value === "high") {
528 bits = 4096;
529 } else { // medium
530 bits = 2048;
531 }
532 }
533
534 cenroll.GenKeyFlags = bits << 16; // keysize is encoded in the uper 16 bits
535 //cenroll.GenKeyFlags = cenroll.GenKeyFlags | 0x1; //CRYPT_EXPORTABLE
536
537 generatingKeyNotice.style.display = "";
538
539 // The request needs to be created after we return so the "please wait"
540 // message gets rendered
541 var createCSRHandler = function () {
542 try {
543 csr.value = cenroll.createPKCS10("", "1.3.6.1.5.5.7.3.2");
544 form.submit();
545 } catch (e) {
546 if (e.number === -2147023673) {
547 // 0x800704c7 => dialogue declined
548 showError(createRequestErrorConfirmDialogue.innerHTML, e);
549 } else if (e.number === -2146435043) {
550 // 0x8010001d => crypto-device not connected
551 showError(createRequestErrorConnectDevice.innerHTML, e);
552 } else {
553 showError(createRequestError.innerHTML, e);
554 }
555 }
556
557 generatingKeyNotice.style.display = "none";
558 cenroll.Reset();
559 }
560
561 window.setTimeout(createCSRHandler, 0);
562
563 // Always return false, form is submitted by deferred method
564 return false;
565 }
566
567 /// Call if securityLevel has changed
568 var refreshSecurityLevel = function () {
569 var level = securityLevel.options[securityLevel.selectedIndex];
570 if (level.value === "custom") {
571 getProviderList();
572 customSettings.style.display = "";
573 } else {
574 customSettings.style.display = "none";
575 }
576 }
577
578 securityLevel.onchange = refreshSecurityLevel;
579 provider.onchange = getAlgorithmList;
580 algorithm.onchange = getKeySizeLimits;
581 genReq.onclick = createCSR;
582
583 return true;
584 };
585
586 // Run the init functions until one is successful
587 if (initCertEnroll()) {
588 form.style.display = "";
589 noActiveX.style.display = "none";
590 } else if (initXEnroll()) {
591 form.style.display = "";
592 noActiveX.style.display = "none";
593 } else {
594 window.alert(unsupportedPlatformError.innerHTML);
595 }
596 } ();