bug 1236: Use single assignment for global variables
[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 (
132 (bits < alg.MinLength) ||
133 (bits > alg.MaxLength) ||
134 (
135 alg.IncrementLength &&
136 ((bits - alg.MinLength) % alg.IncrementLength !== 0)
137 )
138 ) {
139 return false;
140 }
141
142 return bits;
143 }
144
145 /// Fill the key size list
146 var getKeySizeList = function () {
147 if (!cspStats) {
148 return false;
149 }
150
151 var alg = getAlgorithm();
152
153 // HTML5 attributes
154 keySize.setAttribute("min", alg.MinLength);
155 keySize.setAttribute("max", alg.MaxLength);
156 keySize.setAttribute("step", alg.IncrementLength);
157 keySize.setAttribute("value", alg.DefaultLength);
158 keySize.value = ""+alg.DefaultLength;
159
160 // ugly, but buggy otherwise if done with text nodes
161 keySizeMin.innerHTML = alg.MinLength;
162 keySizeMax.innerHTML = alg.MaxLength;
163 keySizeStep.innerHTML = alg.IncrementLength;
164
165 return true;
166 }
167
168 /// Fill the algorithm list
169 var getAlgorithmList = function () {
170 var i;
171
172 if (!providerList) {
173 return false;
174 }
175
176 var csp = getProvider();
177
178 cspStats = providerList.GetCspStatusesFromOperations(
179 0x1c, //XCN_NCRYPT_ANY_ASYMMETRIC_OPERATION
180 //0x10, //XCN_NCRYPT_SIGNATURE_OPERATION
181 //0x8, //XCN_NCRYPT_SECRET_AGREEMENT_OPERATION
182 //0x4, //XCN_NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION
183 csp
184 );
185
186 removeChildren(algorithm);
187 for (i = 0; i < cspStats.Count; i++) {
188 var alg = cspStats.ItemByIndex(i).CspAlgorithm;
189 algorithm.appendChild(option(alg.Name, i));
190 }
191
192 return getKeySizeList();
193 }
194
195 /// Fill the crypto provider list
196 var getProviderList = function () {
197 var i;
198
199 var csps = factory.CreateObject("X509Enrollment.CCspInformations");
200
201 // Get provider information
202 csps.AddAvailableCsps();
203
204 removeChildren(provider);
205
206 for (i = 0; i < csps.Count; i++) {
207 var csp = csps.ItemByIndex(i);
208 provider.appendChild(option(csp.Name, i));
209 }
210
211 providerList = csps;
212
213 return getAlgorithmList();
214 }
215
216 /// Generate a key and create and submit the actual CSR
217 var createCSR = function () {
218 var providerName, algorithmOid, bits;
219
220 var level = securityLevel.options[securityLevel.selectedIndex];
221 if (level.value === "custom") {
222 providerName = getProvider().Name;
223 var alg = getAlgorithm();
224 algorithmOid = alg.GetAlgorithmOid(0, 0)
225 bits = getKeySize();
226 if (!bits) {
227 window.alert(invalidKeySizeError.innerHTML);
228 return false;
229 }
230 } else {
231 providerName = "Microsoft Software Key Storage Provider";
232
233 algorithmOid = factory.CreateObject("X509Enrollment.CObjectId");
234 algorithmOid.InitializeFromValue("1.2.840.113549.1.1.1"); // RSA
235 // "1.2.840.10040.4.1" == DSA
236 // "1.2.840.10046.2.1" == DH
237
238 if (level.value === "high") {
239 bits = 4096;
240 } else { // medium
241 bits = 2048;
242 }
243 }
244
245 var privateKey = factory.CreateObject("X509Enrollment.CX509PrivateKey");
246 privateKey.ProviderName = providerName;
247 privateKey.Algorithm = algorithmOid;
248 privateKey.Length = bits;
249 privateKey.KeyUsage = 0xffffff; // XCN_NCRYPT_ALLOW_ALL_USAGES
250
251 var request = factory.CreateObject("X509Enrollment.CX509CertificateRequestPkcs10");
252 request.InitializeFromPrivateKey(
253 1, // ContextUser
254 privateKey,
255 "" // don't use a template
256 );
257
258 var enroll = factory.CreateObject("X509Enrollment.CX509Enrollment");
259 enroll.InitializeFromRequest(request);
260
261 generatingKeyNotice.style.display = "";
262
263 // The request needs to be created after we return so the "please wait"
264 // message gets rendered
265 var createCSRHandler = function () {
266 try {
267 csr.value = enroll.CreateRequest(0x1); //XCN_CRYPT_STRING_BASE64
268 form.submit();
269 } catch (e) {
270 showError(createRequestErrorChooseAlgorithm.innerHTML, e);
271 }
272
273 generatingKeyNotice.style.display = "none";
274 }
275
276 window.setTimeout(createCSRHandler, 0);
277
278 // Always return false, form is submitted by deferred method
279 return false;
280 }
281
282 /// Call if securityLevel has changed
283 var refreshSecurityLevel = function () {
284 var level = securityLevel.options[securityLevel.selectedIndex];
285 if (level.value === "custom") {
286 getProviderList();
287 customSettings.style.display = "";
288 } else {
289 customSettings.style.display = "none";
290 }
291 }
292
293 securityLevel.onchange = refreshSecurityLevel;
294 provider.onchange = getAlgorithmList;
295 algorithm.onchange = getKeySizeList;
296 genReq.onclick = createCSR;
297
298 return true;
299 } // end of initCertEnroll()
300
301 /// Initialise Xenroll code (XP and lower)
302 /// returns false if initialisation fails
303 var initXEnroll = function () {
304 cenroll = null;
305
306 providerTypes = Array(
307 1, //PROV_RSA_FULL
308 2, //PROV_RSA_SIG
309 3, //PROV_DSS
310 4, //PROV_FORTEZZA
311 5, //PROV_MS_EXCHANGE
312 6, //PROV_SSL
313 12, //PROV_RSA_SCHANNEL
314 13, //PROV_DSS_DH
315 14, //PROV_EC_ECDSA_SIG
316 15, //PROV_EC_ECNRA_SIG
317 16, //PROV_EC_ECDSA_FULL
318 17, //PROV_EC_ECNRA_FULL
319 18, //PROV_DH_SCHANNEL
320 20, //PROV_SPYRUS_LYNKS
321 21, //PROV_RNG
322 22, //PROV_INTEL_SEC
323 23, //PROV_REPLACE_OWF
324 24 //PROV_RSA_AES
325 );
326
327 algClasses = Array(
328 1 << 13, //ALG_CLASS_SIGNATURE
329 //2 << 13, //ALG_CLASS_MSG_ENCRYPT
330 //3 << 13, //ALG_CLASS_DATA_ENCRYPT
331 //4 << 13, //ALG_CLASS_HASH
332 5 << 13 //ALG_CLASS_KEY_EXCHANGE
333 );
334
335 // Try to initialise the ActiveX element.
336 try {
337 cenroll = new ActiveXObject("CEnroll.CEnroll");
338
339 if (!cenroll) {
340 throw {
341 name: "NoObjectError",
342 message: "Got null at object creation"
343 };
344 }
345
346 form.style.display = "";
347 algorithm.disabled = true;
348 noActiveX.style.display = "none";
349 } catch (e) {
350 return false;
351 }
352
353 /// Get the name of the selected provider
354 var getProviderName = function () {
355 return provider.options[provider.selectedIndex].text;
356 }
357
358 /// Get the type of the selected provider
359 var getProviderType = function () {
360 return parseInt(provider.options[provider.selectedIndex].value, 10);
361 }
362
363 var refreshProvider = function () {
364 cenroll.ProviderName = getProviderName();
365 cenroll.ProviderType = getProviderType();
366 }
367
368 /// Get the ID of the selected algorithm
369 var getAlgorithmId = function () {
370 return parseInt(algorithm.options[algorithm.selectedIndex].value, 10);
371 }
372
373 /// Minimum bit length for exchange keys
374 var getMinExKeyLength = function () {
375 refreshProvider();
376
377 try {
378 return cenroll.GetKeyLen(true, true);
379 } catch (e) {
380 return false;
381 }
382 }
383
384 /// Maximum bit length for exchange keys
385 var getMaxExKeyLength = function () {
386 refreshProvider();
387
388 try {
389 return cenroll.GetKeyLen(false, true);
390 } catch (e) {
391 return false;
392 }
393 }
394
395 /// Step size for exchange keys
396 /// This might not be available on older platforms
397 var getStepExKeyLength = function () {
398 refreshProvider();
399
400 try {
401 return cenroll.GetKeyLenEx(3, 1);
402 } catch (e) {
403 return false;
404 }
405 }
406
407 /// Minimum bit length for signature keys
408 var getMinSigKeyLength = function () {
409 refreshProvider();
410
411 try {
412 return cenroll.GetKeyLen(true, false);
413 } catch (e) {
414 return false;
415 }
416 }
417
418 /// Maximum bit length for signature keys
419 var getMaxSigKeyLength = function () {
420 refreshProvider();
421
422 try {
423 return cenroll.GetKeyLen(false, false);
424 } catch (e) {
425 return false;
426 }
427 }
428
429 /// Step size for signature keys
430 /// This might not be available on older platforms
431 var getStepSigKeyLength = function () {
432 refreshProvider();
433
434 try {
435 return cenroll.GetKeyLenEx(3, 2);
436 } catch (e) {
437 return false;
438 }
439 }
440
441 /// Get the selected key size
442 var getKeySize = function () {
443 var bits = parseInt(keySize.value, 10);
444 if (
445 (bits < getMinSigKeyLength()) ||
446 (bits > getMaxSigKeyLength()) ||
447 (
448 getStepSigKeyLength() &&
449 ((bits - getMinSigKeyLength()) % getStepSigKeyLength() !== 0)
450 )
451 ) {
452 return false;
453 }
454
455 return bits;
456 }
457
458 var getKeySizeLimits = function () {
459 // HTML5 attributes
460 keySize.setAttribute("min", getMinSigKeyLength());
461 keySize.setAttribute("max", getMaxSigKeyLength());
462 if (getStepSigKeyLength()) {
463 keySize.setAttribute("step", getStepSigKeyLength());
464 }
465
466 // ugly, but buggy otherwise if done with text nodes
467 keySizeMin.innerHTML = getMinSigKeyLength();
468 keySizeMax.innerHTML = getMaxSigKeyLength();
469 keySizeStep.innerHTML = getStepSigKeyLength();
470
471 if (getMinSigKeyLength() === getMaxSigKeyLength()) {
472 keySize.value = getMaxSigKeyLength();
473 }
474
475 return true;
476 }
477
478 /// Fill the algorithm selection box
479 var getAlgorithmList = function () {
480 var i, j;
481
482 refreshProvider();
483
484 removeChildren(algorithm);
485
486 for (i = 0; i < algClasses.length; ++i) {
487 for (j = 0; true; ++j) {
488 try {
489 var algId = cenroll.EnumAlgs(j, algClasses[i]);
490 var algName = cenroll.GetAlgName(algId);
491 algorithm.appendChild(option(algName, algId));
492 } catch (e) {
493 break;
494 }
495 }
496 }
497
498 getKeySizeLimits();
499 }
500
501 /// Fill the provider selection box
502 var getProviderList = function () {
503 var i, j;
504
505 removeChildren(provider);
506
507 for (i = 0; i < providerTypes.length; ++i) {
508 cenroll.providerType = providerTypes[i];
509
510 var providerName = "invalid";
511 for (j = 0; true; ++j) {
512 try {
513 providerName = cenroll.enumProviders(j, 0);
514 provider.appendChild(option(providerName, providerTypes[i]));
515 } catch (e) {
516 break;
517 }
518 }
519 }
520
521 return getAlgorithmList();
522 }
523
524 var createCSR = function () {
525 var providerName, bits;
526
527 var level = securityLevel.options[securityLevel.selectedIndex];
528 if (level.value === "custom") {
529 refreshProvider();
530
531 bits = getKeySize();
532 if (bits === false) {
533 window.alert(invalidKeySizeError.innerHTML);
534 return false;
535 }
536 } else {
537 cenroll.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
538 cenroll.ProviderType = 1; //PROV_RSA_FULL
539
540 if (level.value === "high") {
541 bits = 4096;
542 } else { // medium
543 bits = 2048;
544 }
545 }
546
547 cenroll.GenKeyFlags = bits << 16; // keysize is encoded in the uper 16 bits
548 //cenroll.GenKeyFlags = cenroll.GenKeyFlags | 0x1; //CRYPT_EXPORTABLE
549
550 generatingKeyNotice.style.display = "";
551
552 // The request needs to be created after we return so the "please wait"
553 // message gets rendered
554 var createCSRHandler = function () {
555 try {
556 csr.value = cenroll.createPKCS10("", "1.3.6.1.5.5.7.3.2");
557 form.submit();
558 } catch (e) {
559 if (e.number === -2147023673) {
560 // 0x800704c7 => dialogue declined
561 showError(createRequestErrorConfirmDialogue.innerHTML, e);
562 } else if (e.number === -2146435043) {
563 // 0x8010001d => crypto-device not connected
564 showError(createRequestErrorConnectDevice.innerHTML, e);
565 } else {
566 showError(createRequestError.innerHTML, e);
567 }
568 }
569
570 generatingKeyNotice.style.display = "none";
571 cenroll.Reset();
572 }
573
574 window.setTimeout(createCSRHandler, 0);
575
576 // Always return false, form is submitted by deferred method
577 return false;
578 }
579
580 /// Call if securityLevel has changed
581 var refreshSecurityLevel = function () {
582 var level = securityLevel.options[securityLevel.selectedIndex];
583 if (level.value === "custom") {
584 getProviderList();
585 customSettings.style.display = "";
586 } else {
587 customSettings.style.display = "none";
588 }
589 }
590
591 securityLevel.onchange = refreshSecurityLevel;
592 provider.onchange = getAlgorithmList;
593 algorithm.onchange = getKeySizeLimits;
594 genReq.onclick = createCSR;
595
596 return true;
597 };
598
599 // Run the init functions until one is successful
600 if (initCertEnroll()) {
601 form.style.display = "";
602 noActiveX.style.display = "none";
603 } else if (initXEnroll()) {
604 form.style.display = "";
605 noActiveX.style.display = "none";
606 } else {
607 window.alert(unsupportedPlatformError.innerHTML);
608 }
609 } ();