bug 964: Make the structure of some queries more easy to read
[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 if (!providerList) {
171 return false;
172 }
173
174 var csp = getProvider();
175
176 cspStats = providerList.GetCspStatusesFromOperations(
177 0x1c, //XCN_NCRYPT_ANY_ASYMMETRIC_OPERATION
178 //0x10, //XCN_NCRYPT_SIGNATURE_OPERATION
179 //0x8, //XCN_NCRYPT_SECRET_AGREEMENT_OPERATION
180 //0x4, //XCN_NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION
181 csp
182 );
183
184 removeChildren(algorithm);
185 for (var i = 0; i < cspStats.Count; i++) {
186 var alg = cspStats.ItemByIndex(i).CspAlgorithm;
187 algorithm.appendChild(option(alg.Name, i));
188 }
189
190 return getKeySizeList();
191 }
192
193 /// Fill the crypto provider list
194 var getProviderList = function () {
195 var csps = factory.CreateObject("X509Enrollment.CCspInformations");
196
197 // Get provider information
198 csps.AddAvailableCsps();
199
200 removeChildren(provider);
201
202 for (var 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("X509Enrollment.CX509CertificateRequestPkcs10");
248 request.InitializeFromPrivateKey(
249 1, // ContextUser
250 privateKey,
251 "" // don't use a template
252 );
253
254 var enroll = factory.CreateObject("X509Enrollment.CX509Enrollment");
255 enroll.InitializeFromRequest(request);
256
257 generatingKeyNotice.style.display = "";
258
259 // The request needs to be created after we return so the "please wait"
260 // message gets rendered
261 var createCSRHandler = function () {
262 try {
263 csr.value = enroll.CreateRequest(0x1); //XCN_CRYPT_STRING_BASE64
264 form.submit();
265 } catch (e) {
266 showError(createRequestErrorChooseAlgorithm.innerHTML, e);
267 }
268
269 generatingKeyNotice.style.display = "none";
270 }
271
272 window.setTimeout(createCSRHandler, 0);
273
274 // Always return false, form is submitted by deferred method
275 return false;
276 }
277
278 /// Call if securityLevel has changed
279 var refreshSecurityLevel = function () {
280 var level = securityLevel.options[securityLevel.selectedIndex];
281 if (level.value === "custom") {
282 getProviderList();
283 customSettings.style.display = "";
284 } else {
285 customSettings.style.display = "none";
286 }
287 }
288
289 securityLevel.onchange = refreshSecurityLevel;
290 provider.onchange = getAlgorithmList;
291 algorithm.onchange = getKeySizeList;
292 genReq.onclick = createCSR;
293
294 return true;
295 } // end of initCertEnroll()
296
297 /// Initialise Xenroll code (XP and lower)
298 /// returns false if initialisation fails
299 var initXEnroll = function () {
300 cenroll = null;
301
302 providerTypes = Array(
303 1, //PROV_RSA_FULL
304 2, //PROV_RSA_SIG
305 3, //PROV_DSS
306 4, //PROV_FORTEZZA
307 5, //PROV_MS_EXCHANGE
308 6, //PROV_SSL
309 12, //PROV_RSA_SCHANNEL
310 13, //PROV_DSS_DH
311 14, //PROV_EC_ECDSA_SIG
312 15, //PROV_EC_ECNRA_SIG
313 16, //PROV_EC_ECDSA_FULL
314 17, //PROV_EC_ECNRA_FULL
315 18, //PROV_DH_SCHANNEL
316 20, //PROV_SPYRUS_LYNKS
317 21, //PROV_RNG
318 22, //PROV_INTEL_SEC
319 23, //PROV_REPLACE_OWF
320 24 //PROV_RSA_AES
321 );
322
323 algClasses = Array(
324 1 << 13, //ALG_CLASS_SIGNATURE
325 //2 << 13, //ALG_CLASS_MSG_ENCRYPT
326 //3 << 13, //ALG_CLASS_DATA_ENCRYPT
327 //4 << 13, //ALG_CLASS_HASH
328 5 << 13 //ALG_CLASS_KEY_EXCHANGE
329 );
330
331 // Try to initialise the ActiveX element.
332 try {
333 cenroll = new ActiveXObject("CEnroll.CEnroll");
334
335 if (!cenroll) {
336 throw {
337 name: "NoObjectError",
338 message: "Got null at object creation"
339 };
340 }
341
342 form.style.display = "";
343 algorithm.disabled = true;
344 noActiveX.style.display = "none";
345 } catch (e) {
346 return false;
347 }
348
349 /// Get the name of the selected provider
350 var getProviderName = function () {
351 return provider.options[provider.selectedIndex].text;
352 }
353
354 /// Get the type of the selected provider
355 var getProviderType = function () {
356 return parseInt(provider.options[provider.selectedIndex].value, 10);
357 }
358
359 var refreshProvider = function () {
360 cenroll.ProviderName = getProviderName();
361 cenroll.ProviderType = getProviderType();
362 }
363
364 /// Get the ID of the selected algorithm
365 var getAlgorithmId = function () {
366 return parseInt(algorithm.options[algorithm.selectedIndex].value, 10);
367 }
368
369 /// Minimum bit length for exchange keys
370 var getMinExKeyLength = function () {
371 refreshProvider();
372
373 try {
374 return cenroll.GetKeyLen(true, true);
375 } catch (e) {
376 return false;
377 }
378 }
379
380 /// Maximum bit length for exchange keys
381 var getMaxExKeyLength = function () {
382 refreshProvider();
383
384 try {
385 return cenroll.GetKeyLen(false, true);
386 } catch (e) {
387 return false;
388 }
389 }
390
391 /// Step size for exchange keys
392 /// This might not be available on older platforms
393 var getStepExKeyLength = function () {
394 refreshProvider();
395
396 try {
397 return cenroll.GetKeyLenEx(3, 1);
398 } catch (e) {
399 return false;
400 }
401 }
402
403 /// Minimum bit length for signature keys
404 var getMinSigKeyLength = function () {
405 refreshProvider();
406
407 try {
408 return cenroll.GetKeyLen(true, false);
409 } catch (e) {
410 return false;
411 }
412 }
413
414 /// Maximum bit length for signature keys
415 var getMaxSigKeyLength = function () {
416 refreshProvider();
417
418 try {
419 return cenroll.GetKeyLen(false, false);
420 } catch (e) {
421 return false;
422 }
423 }
424
425 /// Step size for signature keys
426 /// This might not be available on older platforms
427 var getStepSigKeyLength = function () {
428 refreshProvider();
429
430 try {
431 return cenroll.GetKeyLenEx(3, 2);
432 } catch (e) {
433 return false;
434 }
435 }
436
437 /// Get the selected key size
438 var getKeySize = function () {
439 var bits = parseInt(keySize.value, 10);
440 if (
441 (bits < getMinSigKeyLength()) ||
442 (bits > getMaxSigKeyLength()) ||
443 (
444 getStepSigKeyLength() &&
445 ((bits - getMinSigKeyLength()) % 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 refreshProvider();
477
478 removeChildren(algorithm);
479
480 for (var i = 0; i < algClasses.length; ++i) {
481 for (var j = 0; true; ++j) {
482 try {
483 var algId = cenroll.EnumAlgs(j, algClasses[i]);
484 var algName = cenroll.GetAlgName(algId);
485 algorithm.appendChild(option(algName, algId));
486 } catch (e) {
487 break;
488 }
489 }
490 }
491
492 getKeySizeLimits();
493 }
494
495 /// Fill the provider selection box
496 var getProviderList = function () {
497 removeChildren(provider);
498
499 for (var i = 0; i < providerTypes.length; ++i) {
500 cenroll.providerType = providerTypes[i];
501
502 var providerName = "invalid";
503 for (var j = 0; true; ++j) {
504 try {
505 providerName = cenroll.enumProviders(j, 0);
506 provider.appendChild(option(providerName, providerTypes[i]));
507 } catch (e) {
508 break;
509 }
510 }
511 }
512
513 return getAlgorithmList();
514 }
515
516 var createCSR = function () {
517 var providerName, bits;
518
519 var level = securityLevel.options[securityLevel.selectedIndex];
520 if (level.value === "custom") {
521 refreshProvider();
522
523 bits = getKeySize();
524 if (bits === false) {
525 window.alert(invalidKeySizeError.innerHTML);
526 return false;
527 }
528 } else {
529 cenroll.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
530 cenroll.ProviderType = 1; //PROV_RSA_FULL
531
532 if (level.value === "high") {
533 bits = 4096;
534 } else { // medium
535 bits = 2048;
536 }
537 }
538
539 cenroll.GenKeyFlags = bits << 16; // keysize is encoded in the uper 16 bits
540 //cenroll.GenKeyFlags = cenroll.GenKeyFlags | 0x1; //CRYPT_EXPORTABLE
541
542 generatingKeyNotice.style.display = "";
543
544 // The request needs to be created after we return so the "please wait"
545 // message gets rendered
546 var createCSRHandler = function () {
547 try {
548 csr.value = cenroll.createPKCS10("", "1.3.6.1.5.5.7.3.2");
549 form.submit();
550 } catch (e) {
551 if (e.number === -2147023673) {
552 // 0x800704c7 => dialogue declined
553 showError(createRequestErrorConfirmDialogue.innerHTML, e);
554 } else if (e.number === -2146435043) {
555 // 0x8010001d => crypto-device not connected
556 showError(createRequestErrorConnectDevice.innerHTML, e);
557 } else {
558 showError(createRequestError.innerHTML, e);
559 }
560 }
561
562 generatingKeyNotice.style.display = "none";
563 cenroll.Reset();
564 }
565
566 window.setTimeout(createCSRHandler, 0);
567
568 // Always return false, form is submitted by deferred method
569 return false;
570 }
571
572 /// Call if securityLevel has changed
573 var refreshSecurityLevel = function () {
574 var level = securityLevel.options[securityLevel.selectedIndex];
575 if (level.value === "custom") {
576 getProviderList();
577 customSettings.style.display = "";
578 } else {
579 customSettings.style.display = "none";
580 }
581 }
582
583 securityLevel.onchange = refreshSecurityLevel;
584 provider.onchange = getAlgorithmList;
585 algorithm.onchange = getKeySizeLimits;
586 genReq.onclick = createCSR;
587
588 return true;
589 };
590
591 // Run the init functions until one is successful
592 if (initCertEnroll()) {
593 form.style.display = "";
594 noActiveX.style.display = "none";
595 } else if (initXEnroll()) {
596 form.style.display = "";
597 noActiveX.style.display = "none";
598 } else {
599 window.alert(unsupportedPlatformError.innerHTML);
600 }
601 } ();