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