608 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			608 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| (function() {
 | |
| 	"use strict";
 | |
| 
 | |
| 	/*global URLSearchParams,Headers*/
 | |
| 	var PromiseA = window.Promise;
 | |
| 	var VERSION = "2";
 | |
| 	// ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
 | |
| 	// ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
 | |
| 	var BROWSER_SUPPORTS_RSA = false;
 | |
| 	var ECDSA_OPTS = { kty: "EC", namedCurve: "P-256" };
 | |
| 	var RSA_OPTS = { kty: "RSA", modulusLength: 2048 };
 | |
| 	var Promise = window.Promise;
 | |
| 	var Keypairs = window.Keypairs;
 | |
| 	var ACME = window.ACME;
 | |
| 	var CSR = window.CSR;
 | |
| 	var $qs = function(s) {
 | |
| 		return window.document.querySelector(s);
 | |
| 	};
 | |
| 	var $qsa = function(s) {
 | |
| 		return window.document.querySelectorAll(s);
 | |
| 	};
 | |
| 	var acme;
 | |
| 	var info = {};
 | |
| 	var steps = {};
 | |
| 	var i = 1;
 | |
| 	var apiUrl = "https://acme-{{env}}.api.letsencrypt.org/directory";
 | |
| 
 | |
| 	// fix previous browsers
 | |
| 	var isCurrent = localStorage.getItem("version") === VERSION;
 | |
| 	if (!isCurrent) {
 | |
| 		localStorage.clear();
 | |
| 		localStorage.setItem("version", VERSION);
 | |
| 	}
 | |
| 	localStorage.setItem("version", VERSION);
 | |
| 
 | |
| 	function updateApiType() {
 | |
| 		/*jshint validthis: true */
 | |
| 		var input =
 | |
| 			this ||
 | |
| 			Array.prototype.filter.call($qsa(".js-acme-api-type"), function($el) {
 | |
| 				return $el.checked;
 | |
| 			})[0];
 | |
| 		//#console.log('ACME api type radio:', input.value);
 | |
| 		$qs(".js-acme-directory-url").value = apiUrl.replace(
 | |
| 			/{{env}}/g,
 | |
| 			input.value
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	function hideForms() {
 | |
| 		$qsa(".js-acme-form").forEach(function(el) {
 | |
| 			el.hidden = true;
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	function updateProgress(currentStep) {
 | |
| 		var progressSteps = $qs("#js-progress-bar").children;
 | |
| 		var j;
 | |
| 		for (j = 0; j < progressSteps.length; j += 1) {
 | |
| 			if (j < currentStep) {
 | |
| 				progressSteps[j].classList.add("js-progress-step-complete");
 | |
| 				progressSteps[j].classList.remove("js-progress-step-started");
 | |
| 			} else if (j === currentStep) {
 | |
| 				progressSteps[j].classList.remove("js-progress-step-complete");
 | |
| 				progressSteps[j].classList.add("js-progress-step-started");
 | |
| 			} else {
 | |
| 				progressSteps[j].classList.remove("js-progress-step-complete");
 | |
| 				progressSteps[j].classList.remove("js-progress-step-started");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	function newAlert(str) {
 | |
| 		return new Promise(function() {
 | |
| 			setTimeout(function() {
 | |
| 				window.alert(str);
 | |
| 				if (window.confirm("Start over?")) {
 | |
| 					document.location.href = document.location.href.replace(
 | |
| 						/\/app.*/,
 | |
| 						"/"
 | |
| 					);
 | |
| 				}
 | |
| 			}, 10);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	function submitForm(ev) {
 | |
| 		var j = i;
 | |
| 		i += 1;
 | |
| 
 | |
| 		return PromiseA.resolve()
 | |
| 			.then(function() {
 | |
| 				return steps[j].submit(ev);
 | |
| 			})
 | |
| 			.catch(function(err) {
 | |
| 				var ourfault = true;
 | |
| 				console.error(err);
 | |
| 				if (/failed to fetch/i.test(err.message)) {
 | |
| 					return newAlert("Network connection failure.");
 | |
| 				}
 | |
| 
 | |
| 				if ("E_ACME_CHALLENGE" === err.code) {
 | |
| 					if ("dns-01" === err.type) {
 | |
| 						ourfault = false;
 | |
| 						return newAlert(
 | |
| 							"It looks like the DNS record you set for " +
 | |
| 								err.altname +
 | |
| 								" was incorrect or did not propagate. " +
 | |
| 								"The error message was '" +
 | |
| 								err.message +
 | |
| 								"'"
 | |
| 						);
 | |
| 					} else if ("http-01" === err.type) {
 | |
| 						ourfault = false;
 | |
| 						return newAlert(
 | |
| 							"It looks like the file you uploaded for " +
 | |
| 								err.altname +
 | |
| 								" was incorrect or could not be downloaded. " +
 | |
| 								"The error message was '" +
 | |
| 								err.message +
 | |
| 								"'"
 | |
| 						);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if (ourfault) {
 | |
| 					err.auth = undefined;
 | |
| 					window.alert(
 | |
| 						"Something went wrong. It's probably our fault, not yours." +
 | |
| 							" Please email aj@rootprojects.org to let him know. The error message is: \n" +
 | |
| 							JSON.stringify(err, null, 2)
 | |
| 					);
 | |
| 					return new Promise(function() {});
 | |
| 				}
 | |
| 			});
 | |
| 	}
 | |
| 
 | |
| 	function testKeypairSupport() {
 | |
| 		return Keypairs.generate(RSA_OPTS)
 | |
| 			.then(function() {
 | |
| 				console.info("[crypto] RSA is supported");
 | |
| 				BROWSER_SUPPORTS_RSA = true;
 | |
| 			})
 | |
| 			.catch(function() {
 | |
| 				console.warn("[crypto] RSA is NOT supported");
 | |
| 				return Keypairs.generate(ECDSA_OPTS)
 | |
| 					.then(function() {
 | |
| 						console.info("[crypto] ECDSA is supported");
 | |
| 					})
 | |
| 					.catch(function(e) {
 | |
| 						console.warn("[crypto] EC is NOT supported");
 | |
| 						throw e;
 | |
| 					});
 | |
| 			});
 | |
| 	}
 | |
| 
 | |
| 	function getServerKeypair() {
 | |
| 		var sortedAltnames = info.identifiers
 | |
| 			.map(function(ident) {
 | |
| 				return ident.value;
 | |
| 			})
 | |
| 			.sort()
 | |
| 			.join(",");
 | |
| 		var serverJwk = JSON.parse(
 | |
| 			localStorage.getItem("server:" + sortedAltnames) || "null"
 | |
| 		);
 | |
| 		if (serverJwk) {
 | |
| 			return PromiseA.resolve(serverJwk);
 | |
| 		}
 | |
| 
 | |
| 		var keypairOpts;
 | |
| 		// TODO allow for user preference
 | |
| 		if (BROWSER_SUPPORTS_RSA) {
 | |
| 			keypairOpts = RSA_OPTS;
 | |
| 		} else {
 | |
| 			keypairOpts = ECDSA_OPTS;
 | |
| 		}
 | |
| 
 | |
| 		return Keypairs.generate(RSA_OPTS)
 | |
| 			.catch(function(err) {
 | |
| 				console.error(
 | |
| 					"[Error] Keypairs.generate(" + JSON.stringify(RSA_OPTS) + "):"
 | |
| 				);
 | |
| 				throw err;
 | |
| 			})
 | |
| 			.then(function(pair) {
 | |
| 				localStorage.setItem(
 | |
| 					"server:" + sortedAltnames,
 | |
| 					JSON.stringify(pair.private)
 | |
| 				);
 | |
| 				return pair.private;
 | |
| 			});
 | |
| 	}
 | |
| 
 | |
| 	function getAccountKeypair(email) {
 | |
| 		var json = localStorage.getItem("account:" + email);
 | |
| 		if (json) {
 | |
| 			return Promise.resolve(JSON.parse(json));
 | |
| 		}
 | |
| 
 | |
| 		return Keypairs.generate(ECDSA_OPTS)
 | |
| 			.catch(function(err) {
 | |
| 				console.warn(
 | |
| 					"[Error] Keypairs.generate(" + JSON.stringify(ECDSA_OPTS) + "):\n",
 | |
| 					err
 | |
| 				);
 | |
| 				return Keypairs.generate(RSA_OPTS).catch(function(err) {
 | |
| 					console.error(
 | |
| 						"[Error] Keypairs.generate(" + JSON.stringify(RSA_OPTS) + "):"
 | |
| 					);
 | |
| 					throw err;
 | |
| 				});
 | |
| 			})
 | |
| 			.then(function(pair) {
 | |
| 				localStorage.setItem("account:" + email, JSON.stringify(pair.private));
 | |
| 				return pair.private;
 | |
| 			});
 | |
| 	}
 | |
| 
 | |
| 	function updateChallengeType() {
 | |
| 		/*jshint validthis: true*/
 | |
| 		var input =
 | |
| 			this ||
 | |
| 			Array.prototype.filter.call($qsa(".js-acme-challenge-type"), function(
 | |
| 				$el
 | |
| 			) {
 | |
| 				return $el.checked;
 | |
| 			})[0];
 | |
| 		$qs(".js-acme-verification-wildcard").hidden = true;
 | |
| 		$qs(".js-acme-verification-http-01").hidden = true;
 | |
| 		$qs(".js-acme-verification-dns-01").hidden = true;
 | |
| 		if (info.challenges.wildcard) {
 | |
| 			$qs(".js-acme-verification-wildcard").hidden = false;
 | |
| 		}
 | |
| 		if (info.challenges[input.value]) {
 | |
| 			$qs(".js-acme-verification-" + input.value).hidden = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	function saveContact(email, domains) {
 | |
| 		// to be used for good, not evil
 | |
| 		return window
 | |
| 			.fetch(
 | |
| 				"https://api.rootprojects.org/api/rootprojects.org/public/community",
 | |
| 				{
 | |
| 					method: "POST",
 | |
| 					cors: true,
 | |
| 					headers: new Headers({ "Content-Type": "application/json" }),
 | |
| 					body: JSON.stringify({
 | |
| 						address: email,
 | |
| 						project: "greenlock-domains@rootprojects.org",
 | |
| 						timezone: new Intl.DateTimeFormat().resolvedOptions().timeZone,
 | |
| 						domain: domains.join(",")
 | |
| 					})
 | |
| 				}
 | |
| 			)
 | |
| 			.catch(function(err) {
 | |
| 				console.error(err);
 | |
| 			});
 | |
| 	}
 | |
| 
 | |
| 	steps[1] = function() {
 | |
| 		console.info("\n1. Show domains form");
 | |
| 		updateProgress(0);
 | |
| 		hideForms();
 | |
| 		$qs(".js-acme-form-domains").hidden = false;
 | |
| 	};
 | |
| 	steps[1].submit = function() {
 | |
| 		console.info(
 | |
| 			"[submit] 1. Process domains, create ACME client",
 | |
| 			info.domains
 | |
| 		);
 | |
| 		info.domains = $qs(".js-acme-domains")
 | |
| 			.value.replace(/https?:\/\//g, " ")
 | |
| 			.replace(/[,+]/g, " ")
 | |
| 			.trim()
 | |
| 			.split(/\s+/g);
 | |
| 		console.info("[domains]", info.domains.join(" "));
 | |
| 
 | |
| 		info.identifiers = info.domains.map(function(hostname) {
 | |
| 			return { type: "dns", value: hostname.toLowerCase().trim() };
 | |
| 		});
 | |
| 		info.identifiers.sort(function(a, b) {
 | |
| 			if (a === b) {
 | |
| 				return 0;
 | |
| 			}
 | |
| 			if (a < b) {
 | |
| 				return 1;
 | |
| 			}
 | |
| 			if (a > b) {
 | |
| 				return -1;
 | |
| 			}
 | |
| 		});
 | |
| 
 | |
| 		var acmeDirectoryUrl = $qs(".js-acme-directory-url").value;
 | |
| 		acme = ACME.create({ Keypairs: Keypairs, CSR: CSR });
 | |
| 		return acme.init(acmeDirectoryUrl).then(function(directory) {
 | |
| 			$qs(".js-acme-tos-url").href = directory.meta.termsOfService;
 | |
| 			return steps[i]();
 | |
| 		});
 | |
| 	};
 | |
| 
 | |
| 	steps[2] = function() {
 | |
| 		console.info("\n2. Show account (email, ToS) form");
 | |
| 
 | |
| 		updateProgress(0);
 | |
| 		hideForms();
 | |
| 		$qs(".js-acme-form-account").hidden = false;
 | |
| 	};
 | |
| 	steps[2].submit = function() {
 | |
| 		console.info("[submit] 2. Create ACME account (get Key ID)");
 | |
| 
 | |
| 		var email = $qs(".js-acme-account-email")
 | |
| 			.value.toLowerCase()
 | |
| 			.trim();
 | |
| 		info.email = email;
 | |
| 		info.contact = ["mailto:" + email];
 | |
| 		info.agree = $qs(".js-acme-account-tos").checked;
 | |
| 		//info.greenlockAgree = $qs('.js-gl-tos').checked;
 | |
| 
 | |
| 		// TODO ping with version and account creation
 | |
| 		setTimeout(saveContact, 100, email, info.domains);
 | |
| 
 | |
| 		$qs(".js-account-next").disabled = true;
 | |
| 
 | |
| 		return info.cryptoCheck
 | |
| 			.then(function() {
 | |
| 				return getAccountKeypair(email).then(function(jwk) {
 | |
| 					// TODO save account id rather than always retrieving it?
 | |
| 					console.info("[accounts] upsert for", email);
 | |
| 					return acme.accounts
 | |
| 						.create({
 | |
| 							email: email,
 | |
| 							agreeToTerms: info.agree && true,
 | |
| 							accountKeypair: { privateKeyJwk: jwk }
 | |
| 						})
 | |
| 						.then(function(account) {
 | |
| 							console.info("[accounts] result:", account);
 | |
| 							info.account = account;
 | |
| 							info.privateJwk = jwk;
 | |
| 							info.email = email;
 | |
| 						})
 | |
| 						.catch(function(err) {
 | |
| 							console.error("[accounts] failed to upsert account:");
 | |
| 							console.error(err);
 | |
| 							return newAlert(err.message || JSON.stringify(err, null, 2));
 | |
| 						});
 | |
| 				});
 | |
| 			})
 | |
| 			.then(function() {
 | |
| 				var jwk = info.privateJwk;
 | |
| 				var account = info.account;
 | |
| 
 | |
| 				console.info("[orders] requesting");
 | |
| 				return acme.orders
 | |
| 					.request({
 | |
| 						account: account,
 | |
| 						accountKeypair: { privateKeyJwk: jwk },
 | |
| 						domains: info.domains
 | |
| 					})
 | |
| 					.then(function(order) {
 | |
| 						info.order = order;
 | |
| 						console.info("[orders] created ", order);
 | |
| 
 | |
| 						var claims = order.claims;
 | |
| 
 | |
| 						var obj = { "dns-01": [], "http-01": [], wildcard: [] };
 | |
| 						info.challenges = obj;
 | |
| 
 | |
| 						var $httpList = $qs(".js-acme-http");
 | |
| 						var $dnsList = $qs(".js-acme-dns");
 | |
| 						var $wildList = $qs(".js-acme-wildcard");
 | |
| 						var httpTpl = $httpList.innerHTML;
 | |
| 						var dnsTpl = $dnsList.innerHTML;
 | |
| 						var wildTpl = $wildList.innerHTML;
 | |
| 						$httpList.innerHTML = "";
 | |
| 						$dnsList.innerHTML = "";
 | |
| 						$wildList.innerHTML = "";
 | |
| 
 | |
| 						claims.forEach(function(claim) {
 | |
| 							//#console.log("claims[i]", claim);
 | |
| 							var hostname = claim.identifier.value;
 | |
| 							claim.challenges.forEach(function(c) {
 | |
| 								var auth = c;
 | |
| 								var data = {
 | |
| 									type: c.type,
 | |
| 									hostname: hostname,
 | |
| 									url: c.url,
 | |
| 									token: c.token,
 | |
| 									httpPath: auth.challengeUrl,
 | |
| 									httpAuth: auth.keyAuthorization,
 | |
| 									dnsType: "TXT",
 | |
| 									dnsHost: auth.dnsHost,
 | |
| 									dnsAnswer: auth.keyAuthorizationDigest
 | |
| 								};
 | |
| 								//#console.log("claims[i].challenge", data);
 | |
| 
 | |
| 								var $tpl = document.createElement("div");
 | |
| 								if (claim.wildcard) {
 | |
| 									obj.wildcard.push(data);
 | |
| 									$tpl.innerHTML = wildTpl;
 | |
| 									$tpl.querySelector(".js-acme-ver-txt-host").innerHTML =
 | |
| 										data.dnsHost;
 | |
| 									$tpl.querySelector(".js-acme-ver-txt-value").innerHTML =
 | |
| 										data.dnsAnswer;
 | |
| 									$wildList.appendChild($tpl);
 | |
| 								} else if (obj[data.type]) {
 | |
| 									obj[data.type].push(data);
 | |
| 
 | |
| 									if ("dns-01" === data.type) {
 | |
| 										$tpl.innerHTML = dnsTpl;
 | |
| 										$tpl.querySelector(".js-acme-ver-txt-host").innerHTML =
 | |
| 											data.dnsHost;
 | |
| 										$tpl.querySelector(".js-acme-ver-txt-value").innerHTML =
 | |
| 											data.dnsAnswer;
 | |
| 										$dnsList.appendChild($tpl);
 | |
| 									} else if ("http-01" === data.type) {
 | |
| 										$tpl.innerHTML = httpTpl;
 | |
| 										$tpl.querySelector(
 | |
| 											".js-acme-ver-file-location"
 | |
| 										).innerHTML = data.httpPath.split("/").slice(-1);
 | |
| 										$tpl.querySelector(".js-acme-ver-content").innerHTML =
 | |
| 											data.httpAuth;
 | |
| 										$tpl.querySelector(".js-acme-ver-uri").innerHTML =
 | |
| 											data.httpPath;
 | |
| 										$tpl.querySelector(".js-download-verify-link").href =
 | |
| 											"data:text/octet-stream;base64," +
 | |
| 											window.btoa(data.httpAuth);
 | |
| 										$tpl.querySelector(
 | |
| 											".js-download-verify-link"
 | |
| 										).download = data.httpPath.split("/").slice(-1);
 | |
| 										$httpList.appendChild($tpl);
 | |
| 									}
 | |
| 								}
 | |
| 							});
 | |
| 						});
 | |
| 
 | |
| 						// hide wildcard if no wildcard
 | |
| 						// hide http-01 and dns-01 if only wildcard
 | |
| 						if (!obj.wildcard.length) {
 | |
| 							$qs(".js-acme-wildcard-challenges").hidden = true;
 | |
| 						}
 | |
| 						if (!obj["http-01"].length) {
 | |
| 							$qs(".js-acme-challenges").hidden = true;
 | |
| 						}
 | |
| 
 | |
| 						console.info("[housekeeping] challenges", info.challenges);
 | |
| 
 | |
| 						updateChallengeType();
 | |
| 						return steps[i]();
 | |
| 					})
 | |
| 					.catch(function(err) {
 | |
| 						if (err.detail || err.urn) {
 | |
| 							console.error("(Probably) User Error:");
 | |
| 							console.error(err);
 | |
| 							return newAlert(
 | |
| 								"There was an error, probably with your email or domain:\n" +
 | |
| 									err.message
 | |
| 							);
 | |
| 						}
 | |
| 						throw err;
 | |
| 					});
 | |
| 			})
 | |
| 			.catch(function(err) {
 | |
| 				console.error("Step '' Error:");
 | |
| 				console.error(err, err.stack);
 | |
| 				return newAlert(
 | |
| 					"An error happened (but it's not your fault)." +
 | |
| 						" Email aj@rootprojects.org to let him know that 'order and get challenges' failed."
 | |
| 				);
 | |
| 			});
 | |
| 	};
 | |
| 
 | |
| 	steps[3] = function() {
 | |
| 		console.info("\n3. Present challenge options");
 | |
| 		updateProgress(1);
 | |
| 		hideForms();
 | |
| 		$qs(".js-acme-form-challenges").hidden = false;
 | |
| 	};
 | |
| 	steps[3].submit = function() {
 | |
| 		console.info("[submit] 3. Fulfill challenges, fetch certificate");
 | |
| 
 | |
| 		var challengePriority = ["dns-01"];
 | |
| 		if ("http-01" === $qs(".js-acme-challenge-type:checked").value) {
 | |
| 			challengePriority.unshift("http-01");
 | |
| 		}
 | |
| 		console.info("[challenge] selected ", challengePriority[0]);
 | |
| 
 | |
| 		// for now just show the next page immediately (its a spinner)
 | |
| 		steps[i]();
 | |
| 
 | |
| 		return getAccountKeypair(info.email).then(function(jwk) {
 | |
| 			// TODO put a test challenge in the list
 | |
| 			// info.order.claims.push(...)
 | |
| 			// TODO warn about wait-time if DNS
 | |
| 			return getServerKeypair().then(function(serverJwk) {
 | |
| 				return acme.orders
 | |
| 					.complete({
 | |
| 						account: info.account,
 | |
| 						accountKeypair: { privateKeyJwk: jwk },
 | |
| 						order: info.order,
 | |
| 						domains: info.domains,
 | |
| 						domainKeypair: { privateKeyJwk: serverJwk },
 | |
| 						challengePriority: challengePriority,
 | |
| 						challenges: false,
 | |
| 						onChallengeStatus: function(details) {
 | |
| 							$qs(".js-challenge-responses").hidden = false;
 | |
| 							$qs(".js-challenge-response-type").innerText = details.type;
 | |
| 							$qs(".js-challenge-response-status").innerText = details.status;
 | |
| 							$qs(".js-challenge-response-altname").innerText = details.altname;
 | |
| 						}
 | |
| 					})
 | |
| 					.then(function(certs) {
 | |
| 						return Keypairs.export({ jwk: serverJwk }).then(function(keyPem) {
 | |
| 							console.info("WINNING!");
 | |
| 							console.info(certs);
 | |
| 							$qs("#js-fullchain").innerHTML = [
 | |
| 								certs.cert.trim() + "\n",
 | |
| 								certs.chain + "\n"
 | |
| 							].join("\n");
 | |
| 							$qs("#js-download-fullchain-link").href =
 | |
| 								"data:text/octet-stream;base64," + window.btoa(certs);
 | |
| 
 | |
| 							$qs("#js-privkey").innerHTML = keyPem;
 | |
| 							$qs("#js-download-privkey-link").href =
 | |
| 								"data:text/octet-stream;base64," + window.btoa(keyPem);
 | |
| 							return submitForm();
 | |
| 						});
 | |
| 					});
 | |
| 			});
 | |
| 		});
 | |
| 	};
 | |
| 
 | |
| 	// spinner
 | |
| 	steps[4] = function() {
 | |
| 		console.info("\n4. Show loading spinner");
 | |
| 		updateProgress(1);
 | |
| 		hideForms();
 | |
| 		$qs(".js-acme-form-poll").hidden = false;
 | |
| 	};
 | |
| 	steps[4].submit = function() {
 | |
| 		console.info("[submit] 4. Order complete");
 | |
| 
 | |
| 		return steps[i]();
 | |
| 	};
 | |
| 
 | |
| 	steps[5] = function() {
 | |
| 		console.info("\n5. Present certificates (yay!)");
 | |
| 		updateProgress(2);
 | |
| 		hideForms();
 | |
| 		$qs(".js-acme-form-download").hidden = false;
 | |
| 	};
 | |
| 
 | |
| 	function init() {
 | |
| 		$qsa(".js-acme-api-type").forEach(function($el) {
 | |
| 			$el.addEventListener("change", updateApiType);
 | |
| 		});
 | |
| 		updateApiType();
 | |
| 
 | |
| 		$qsa(".js-acme-form").forEach(function($el) {
 | |
| 			$el.addEventListener("submit", function(ev) {
 | |
| 				ev.preventDefault();
 | |
| 				return submitForm(ev);
 | |
| 			});
 | |
| 		});
 | |
| 
 | |
| 		$qsa(".js-acme-challenge-type").forEach(function($el) {
 | |
| 			$el.addEventListener("change", updateChallengeType);
 | |
| 		});
 | |
| 
 | |
| 		var params = new URLSearchParams(window.location.search);
 | |
| 		var apiType = params.get("acme-api-type") || "staging-v02";
 | |
| 		if (params.has("acme-domains")) {
 | |
| 			$qs(".js-acme-domains").value = params.get("acme-domains");
 | |
| 
 | |
| 			$qsa(".js-acme-api-type").forEach(function(ele) {
 | |
| 				if (ele.value === apiType) {
 | |
| 					ele.checked = true;
 | |
| 				}
 | |
| 			});
 | |
| 
 | |
| 			updateApiType();
 | |
| 			steps[2]();
 | |
| 			return submitForm();
 | |
| 		} else {
 | |
| 			steps[1]();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	init();
 | |
| 	$qs("body").hidden = false;
 | |
| 
 | |
| 	// in the background
 | |
| 	info.cryptoCheck = testKeypairSupport()
 | |
| 		.then(function() {
 | |
| 			console.info("[crypto] self-check: passed");
 | |
| 		})
 | |
| 		.catch(function(err) {
 | |
| 			console.error("[crypto] could not use either RSA nor EC.");
 | |
| 			console.error(err);
 | |
| 			window.alert(
 | |
| 				"Generating secure certificates requires a browser with cryptography support." +
 | |
| 					"Please consider a recent version of Chrome, Firefox, or Safari."
 | |
| 			);
 | |
| 			throw err;
 | |
| 		});
 | |
| })();
 |